mirror of
https://gitlab.com/Shinobi-Systems/ShinobiCE.git
synced 2025-03-09 15:40:15 +00:00
Support Shinobi at https://licenses.shinobi.video/subscribe
This commit is contained in:
parent
ef40f3f231
commit
167603dfb0
118 changed files with 16152 additions and 5441 deletions
30
camera.js
30
camera.js
|
@ -19,20 +19,22 @@ var s = loadLib('process')(process,__dirname)
|
|||
loadLib('extenders')(s)
|
||||
//configuration loader
|
||||
var config = loadLib('config')(s)
|
||||
//language loader
|
||||
var lang = loadLib('language')(s,config)
|
||||
//code test module
|
||||
loadLib('codeTester')(s,config,lang)
|
||||
//basic functions
|
||||
loadLib('basic')(s,config)
|
||||
//language loader
|
||||
var lang = loadLib('language')(s,config)
|
||||
//working directories : videos, streams, fileBin..
|
||||
loadLib('folders')(s,config,lang)
|
||||
//code test module
|
||||
loadLib('codeTester')(s,config,lang)
|
||||
//get version
|
||||
loadLib('version')(s,config,lang)
|
||||
//video processing engine
|
||||
loadLib('ffmpeg')(s,config,function(ffmpeg){
|
||||
loadLib('ffmpeg')(s,config,lang,function(ffmpeg){
|
||||
//ffmpeg coProcessor
|
||||
loadLib('ffmpegCoProcessor')(s,config,lang,ffmpeg)
|
||||
//database connection : mysql, sqlite3..
|
||||
loadLib('sql')(s,config)
|
||||
//working directories : videos, streams, fileBin..
|
||||
loadLib('folders')(s,config)
|
||||
//authenticator functions : API, dashboard login..
|
||||
loadLib('auth')(s,config,lang)
|
||||
//express web server with ejs
|
||||
|
@ -48,7 +50,11 @@ loadLib('ffmpeg')(s,config,function(ffmpeg){
|
|||
//websocket connection handlers : login and streams..
|
||||
loadLib('socketio')(s,config,lang,io)
|
||||
//user and group functions
|
||||
loadLib('user')(s,config)
|
||||
loadLib('user')(s,config,lang)
|
||||
//timelapse functions
|
||||
loadLib('timelapse')(s,config,lang,app,io)
|
||||
//fileBin functions
|
||||
loadLib('fileBin')(s,config,lang,app,io)
|
||||
//monitor/camera handlers
|
||||
loadLib('monitor')(s,config,lang)
|
||||
//event functions : motion, object matrix handler
|
||||
|
@ -57,8 +63,10 @@ loadLib('ffmpeg')(s,config,function(ffmpeg){
|
|||
loadLib('detector')(s,config)
|
||||
//recording functions
|
||||
loadLib('videos')(s,config,lang)
|
||||
//branding functions and config defaults
|
||||
loadLib('videoDropInServer')(s,config,lang,app,io)
|
||||
//plugins : websocket connected services..
|
||||
loadLib('plugins')(s,config,lang)
|
||||
loadLib('plugins')(s,config,lang,io)
|
||||
//health : cpu and ram trackers..
|
||||
loadLib('health')(s,config,lang,io)
|
||||
//cluster module
|
||||
|
@ -71,6 +79,10 @@ loadLib('ffmpeg')(s,config,function(ffmpeg){
|
|||
loadLib('rtmpserver')(s,config,lang)
|
||||
//dropInEvents server (file manipulation to create event trigger)
|
||||
loadLib('dropInEvents')(s,config,lang,app,io)
|
||||
//form fields to drive the internals
|
||||
loadLib('definitions')(s,config,lang,app,io)
|
||||
//branding functions and config defaults
|
||||
loadLib('branding')(s,config,lang,app,io)
|
||||
//custom module loader
|
||||
loadLib('customAutoLoad')(s,config,lang,app,io)
|
||||
//scheduling engine
|
||||
|
|
4243
definitions/en_CA.js
Normal file
4243
definitions/en_CA.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,8 @@
|
|||
"Password": "Password",
|
||||
"Password Again": "Password Again",
|
||||
"Remember Me": "Remember Me",
|
||||
"Process Already Running": "Process Already Running",
|
||||
"Process Not Running": "Process Not Running",
|
||||
"RAM": "RAM",
|
||||
"CPU": "CPU",
|
||||
"on": "on",
|
||||
|
@ -26,11 +28,39 @@
|
|||
"Montage": "Montage",
|
||||
"Accounts": "Accounts",
|
||||
"Settings": "Settings",
|
||||
"Cards": "Cards",
|
||||
"No Region": "No Region",
|
||||
"Recording FPS": "Recording FPS",
|
||||
"Input Selector": "Input Selector",
|
||||
"Input Settings": "Input Settings",
|
||||
"Connection": "Connection",
|
||||
"Video Set": "Video Set",
|
||||
"notEnoughFramesText1": "Not Enough Frames for compilation.",
|
||||
"Allow API Trigger": "Allow API Trigger",
|
||||
"When Detector is Off": "When Detector is Off",
|
||||
"When Detector is On": "When Detector is On",
|
||||
"January" : "January",
|
||||
"February" : "February",
|
||||
"March" : "March",
|
||||
"April" : "April",
|
||||
"May": "May",
|
||||
"June" : "June",
|
||||
"July" : "July",
|
||||
"August" : "August",
|
||||
"September" : "September",
|
||||
"October" : "October",
|
||||
"November" : "November",
|
||||
"December" : "December",
|
||||
"Sunday": "Sunday",
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
"Wednesday": "Wednesday",
|
||||
"Thursday": "Thursday",
|
||||
"Friday": "Friday",
|
||||
"Saturday": "Saturday",
|
||||
"Today": "Today",
|
||||
"Always": "Always",
|
||||
"Never": "Never",
|
||||
"API": "API",
|
||||
"ONVIF": "ONVIF",
|
||||
"FFprobe": "Probe",
|
||||
|
@ -53,6 +83,7 @@
|
|||
"Motion Detection": "Motion Detection",
|
||||
"Object Detection": "Object Detection",
|
||||
"JPEG Mode": "JPEG Mode",
|
||||
"Reconnect Stream": "Reconnect Stream",
|
||||
"Order Streams": "Order Streams",
|
||||
"Hide Notes": "Hide Notes",
|
||||
"Example": "Example",
|
||||
|
@ -72,10 +103,11 @@
|
|||
"Connection timed out": "Connection timed out",
|
||||
"skipPingText1": "Try setting \"Skip Ping\" to Yes.",
|
||||
"Ping Failed": "Ping Failed",
|
||||
"Zoom In": "Zoom In <small>URL Address</small>",
|
||||
"Zoom Out": "Zoom Out <small>URL Address</small>",
|
||||
"Zoom In": "Zoom In",
|
||||
"Zoom Out": "Zoom Out",
|
||||
"Enable Nightvision": "Enable Nightvision",
|
||||
"Disable Nightvision": "Disable Nightvision",
|
||||
"Date": "Date",
|
||||
"Current": "Current",
|
||||
"Monitors": "Monitors",
|
||||
"Video": "Video",
|
||||
|
@ -92,6 +124,9 @@
|
|||
"New Monitor": "New Monitor",
|
||||
"Please Check Your Settings": "Please Check Your Settings",
|
||||
"migrateText1": "<b>Input Type</b> could not be parsed. Please set it manually.",
|
||||
"Build": "Build",
|
||||
"Building": "Building",
|
||||
"Started Building": "Started Building",
|
||||
"Add": "Add",
|
||||
"Save": "Save",
|
||||
"Close": "Close",
|
||||
|
@ -207,11 +242,15 @@
|
|||
"Region Name": "Region Name",
|
||||
"Regions": "Regions",
|
||||
"Again": "Again",
|
||||
"Landing Page": "Landing Page",
|
||||
"Account Info": "Account Info",
|
||||
"blankPassword": "Leave blank to keep same password",
|
||||
"2-Factor Authentication": "2-Factor Authentication",
|
||||
"Use Max Storage Amount": "Use Max Storage Amount",
|
||||
"Max Storage Amount": "Max Storage Amount <small>in Megabytes</small>",
|
||||
"Max Storage Amount": "Max Storage Amount",
|
||||
"Video Share": "Video Share",
|
||||
"FileBin Share": "FileBin Share",
|
||||
"Timelapse Frames Share": "Timelapse Frames Share",
|
||||
"Number of Days to keep": "Number of Days to keep",
|
||||
"Monitor Groups": "Monitor Groups",
|
||||
"Group Name": "Group Name",
|
||||
|
@ -228,17 +267,24 @@
|
|||
"Database": "Database",
|
||||
"Database Not Found": "Database Not Found",
|
||||
"User Not Found": "User Not Found",
|
||||
"Not Found": "Not Found",
|
||||
"Save Links to Database": "Save Links to Database",
|
||||
"Uploaders": "Uploaders",
|
||||
"Upload File": "Upload File",
|
||||
"Endpoint": "Endpoint",
|
||||
"Endpoint Address": "Endpoint Address",
|
||||
"Custom Endpoint": "Custom Endpoint",
|
||||
"Bucket": "Bucket",
|
||||
"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",
|
||||
"Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Video Storage",
|
||||
"Use Global WebDAV Video Storage": "Use Global WebDAV Video Storage",
|
||||
"S3-Based Network Storage": "S3-Based Network Storage",
|
||||
"Amazon S3 Upload Error": "Amazon S3 Upload Error",
|
||||
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Hot Cloud Storage Upload Error",
|
||||
"accountId": "Account ID",
|
||||
"keyId": "Key ID",
|
||||
"applicationKey": "Application Key",
|
||||
"aws_accessKeyId": "Access Key Id",
|
||||
"aws_secretAccessKey": "Secret Access Key",
|
||||
|
@ -263,6 +309,8 @@
|
|||
"Currently viewing": "Currently viewing",
|
||||
"Status Indicator": "Status Indicator",
|
||||
"Show Logs": "Show Logs",
|
||||
"Time Occurred": "Time Occurred",
|
||||
"Recent Videos": "Recent Videos",
|
||||
"Videos List": "Videos List",
|
||||
"Monitor Settings": "Monitor Settings",
|
||||
"Enlarge": "Enlarge",
|
||||
|
@ -271,6 +319,7 @@
|
|||
"Idle": "Idle",
|
||||
"Disabled": "Disabled",
|
||||
"Record": "Record",
|
||||
"Watch-Only": "Watch-Only",
|
||||
"Watch Only": "Watch Only",
|
||||
"Toggle Sidebar": "Toggle Sidebar",
|
||||
"Add Monitor": "Add Monitor",
|
||||
|
@ -286,7 +335,7 @@
|
|||
"DeleteSelectedVideosMsg": "Do you want to delete these videos? You cannot recover them.",
|
||||
"ExportSelectedVideosMsg": "Do you want to export these videos? It may take some time to zip and download.",
|
||||
"MergeSelectedVideosMsg": "Do you want to merge these videos? It may take some time to merge and download. The moment the connection is closed the file will be deleted. Ensure you keep the browser open until it is complete.",
|
||||
"clientStreamFailedattemptingReconnect": "Client side ctream check failed, attempting reconnect.",
|
||||
"clientStreamFailedattemptingReconnect": "Client side stream check failed, attempting reconnect.",
|
||||
"Export Video": "Export Video",
|
||||
"Merge Video": "Merge Video",
|
||||
"Delete Filter": "Delete Filter",
|
||||
|
@ -342,6 +391,7 @@
|
|||
"hours": "hours",
|
||||
"a day": "a day",
|
||||
"days": "days",
|
||||
"Days": "Days",
|
||||
"a month": "a month",
|
||||
"months": "months",
|
||||
"a year": "a year",
|
||||
|
@ -349,14 +399,17 @@
|
|||
"Identity": "Identity",
|
||||
"Input": "Input",
|
||||
"Input Feed": "Input Feed",
|
||||
"Timezone": "Timezone",
|
||||
"Timezone Offset": "Timezone Offset",
|
||||
"Stream": "Stream",
|
||||
"Stream Timestamp": "Stream Timestamp",
|
||||
"Stream Watermark": "Stream Watermark",
|
||||
"JPEG API": "JPEG API <small>Snapshot (cgi-bin)</small>",
|
||||
"JPEG API": "JPEG API",
|
||||
"Raw H.264 Stream": "Raw H.264 Stream",
|
||||
"Recording": "Recording",
|
||||
"Recording Timestamp": "Recording Timestamp",
|
||||
"Recording Watermark": "Recording Watermark",
|
||||
"Timelapse Watermark": "Timelapse Watermark",
|
||||
"Region Editor": "Region Editor",
|
||||
"Detector Filters": "Detector Filters",
|
||||
"Filter for Objects only": "Filter for Objects only",
|
||||
|
@ -374,17 +427,23 @@
|
|||
"Refresh List of Cascades": "Refresh List of Cascades",
|
||||
"\"No Motion\" Detector": "\"No Motion\" Detector",
|
||||
"Control": "Control",
|
||||
"Grouping": "Grouping <small>Add groups in <b>Settings</b></small>",
|
||||
"Grouping": "Grouping",
|
||||
"Detector Grouping": "Detector Grouping <small>Add groups in <b>Settings</b></small>",
|
||||
"Detector Recording Process Exited Prematurely. Restarting.": "Detector Recording Process Exited Prematurely. Restarting.",
|
||||
"Detector Recording Complete": "Detector Recording Complete",
|
||||
"Clear Recorder Process": "Clear Recorder Process",
|
||||
"Logging": "Logging",
|
||||
"Timelapse": "Timelapse",
|
||||
"Nothing exists": "Nothing exists",
|
||||
"Already exists": "Already exists",
|
||||
"Creation Interval": "Creation Interval",
|
||||
"Plugin": "Plugin",
|
||||
"IdentityText1": "This is how the system will identify the data for this stream. You cannot change the <b>Monitor ID</b> once you have pressed save. If you want you can make the <b>Monitor ID</b> more human readable before you continue.",
|
||||
"IdentityText2": "You can duplicate a monitor by modifying the <b>Monitor ID</b> then pressing save. You <b>cannot</b> use the ID of a monitor that already exists or it will save over that monitor's database information.",
|
||||
"opencvCascadesText": "If you see nothing here then just download this package of <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascades</a>. Drop them into <code>plugins/opencv/cascades</code> then press refresh <i class=\"fa fa-retweet\"></i>.",
|
||||
"noSpecialCharacters": "No spaces or special characters.",
|
||||
"NotesPlacholder": "Comments you want to leave for this cameras settings.",
|
||||
"InputText1": "This section tells Shinobi how to consume a stream. For optimal performance try tuning your camera's internal settings. Find the following options and set them as shown. To find your camera you can use the <b>built in ONVIF Scanner</b> of Shinobi. Some ONVIF cameras require the use of a management tool to modify their internal settings. If you can't find your cameras you can try <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">ONVIF Device Manager for Windows</a>.",
|
||||
"InputText1": "This section tells Shinobi how to consume a stream. For optimal performance try tuning your camera's internal settings. Find the following options and set them as shown. To find your camera you can use the <b>built in ONVIF Scanner</b> of Shinobi. Some ONVIF cameras require the use of a management tool to modify their internal settings. If you can't find your cameras you can try <a href=\"http://cdn.shinobi.video/installers/odm-v2.2.250.msi\">ONVIF Device Manager for Windows</a>.",
|
||||
"InputText2": "<ul><li><b>Framerate (FPS) :</b> High : 10 - 15 FPS, Low : 2-5 FPS</li><li><b>I-frame interval :</b> 1</li><li><b>Bit Rate Type :</b> VBR (Variable Bit Rate)</li><li><b>Bit Rate :</b> between 256kbps - 1000kbps</li></ul>",
|
||||
"InputText3": "If you need help figuring out what input type your camera is you can take a look in the <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Camera URLs List</a> on the Shinobi website.",
|
||||
"StreamText": "<p>This section will designate the primary method of streaming out and its settings. This stream will be displayed in the dashboard. If you choose to use HLS, JPEG, or MJPEG then you can consume the stream through other programs.</p><p class=\"h_st_input h_st_jpeg\">Using JPEG stream essentially turns off the primary stream and uses the snapshot bin to get frames.</p>",
|
||||
|
@ -410,6 +469,7 @@
|
|||
"HLS Audio Encoder": "Audio Encoder",
|
||||
"HLS Segment Length": "Segment Length <small>in Seconds</small>",
|
||||
"HLS Preset": "Preset Template",
|
||||
"in seconds": "in seconds",
|
||||
"HLS List Size": "List Size",
|
||||
"Traditional Recording": "Traditional Recording",
|
||||
"Recorded Buffer": "Recorded Buffer",
|
||||
|
@ -433,7 +493,7 @@
|
|||
"Position Y": "Position Y",
|
||||
"Image Location": "Image Location <small>Absolute Path or leave blank to use global</small>",
|
||||
"Image Position": "Image Position",
|
||||
"Frame Rate": "Frame Rate <small>(FPS)</small>",
|
||||
"Frame Rate": "Frame Rate",
|
||||
"Image Width": "Image Width",
|
||||
"Image Height": "Image Height",
|
||||
"Record File Type": "Record File Type",
|
||||
|
@ -605,6 +665,8 @@
|
|||
"Restart Core": "Restart Core",
|
||||
"Restart CRON": "Restart CRON",
|
||||
"Flush PM2 Logs": "Flush PM2 Logs",
|
||||
"Controls and Logs": "Controls and Logs",
|
||||
"Delete Logs": "Delete Logs",
|
||||
"Filter ID": "Filter ID",
|
||||
"Webdav Error": "Webdav Error",
|
||||
"WebdavErrorTextTryCreatingDir": "Cannot save. Trying to create directory.",
|
||||
|
@ -694,8 +756,12 @@
|
|||
"Monitor mode is already": "Monitor mode is already",
|
||||
"Monitor or Key does not exist.": "Monitor or Key does not exist.",
|
||||
"No Group with this key exists": "No Group with this key exists",
|
||||
"Downloading Videos": "Downloading Videos",
|
||||
"Zipping Videos": "Zipping Videos",
|
||||
"Success": "Success",
|
||||
"Search Settings": "Search Settings",
|
||||
"Trigger Successful": "Trigger Successful",
|
||||
"Trigger Blocked": "Trigger Blocked",
|
||||
"No such file": "No such file",
|
||||
"h265BrowserText1": "If you are trying to play an H.265 file, you may need to download it and open it in another application like VLC.",
|
||||
"modifyVideoText1": "Method doesn't exist. Check to make sure that the last value of the URL is not blank.",
|
||||
|
@ -711,6 +777,12 @@
|
|||
"Thumbnail": "Thumbnail",
|
||||
"Host Type": "Host Type",
|
||||
"Edit": "Edit",
|
||||
"Next Video": "Next Video",
|
||||
"Previous Video": "Previous Video",
|
||||
"Please Wait...": "Please Wait...",
|
||||
"Select a Monitor": "Select a Monitor",
|
||||
"Per Monitor": "Per Monitor",
|
||||
"Matrices": "Matrices",
|
||||
"Show Matrices": "Show Matrices",
|
||||
"Show Matrix": "Show Matrix",
|
||||
"No Monitor ID Present in Form": "No Monitor ID Present in Form",
|
||||
|
@ -783,6 +855,10 @@
|
|||
"hevc_cuvid": "H.265 CUVID",
|
||||
"mjpeg_cuvid": "MJPEG CUVID",
|
||||
"mpeg4_cuvid": "MPEG4 CUVID",
|
||||
"NVIDIA": "NVIDIA",
|
||||
"VA-API": "VA-API",
|
||||
"Raspberry Pi": "Raspberry Pi",
|
||||
"Quick Sync Video": "Quick Sync Video",
|
||||
"h264_qsv": "H.264 (Quick Sync Video)",
|
||||
"hevc_qsv": "H.265 (Quick Sync Video)",
|
||||
"vp8_qsv": "VP8 (Quick Sync Video)",
|
||||
|
@ -814,7 +890,7 @@
|
|||
"Main": "Main",
|
||||
"Storage Location": "Storage Location",
|
||||
"Recommended": "Recommended",
|
||||
"Please Wait for Completion": "Please Wait for Completion, Depending on the number of files selected this may take some time. Refresh to check again.",
|
||||
"Please Wait for Completion": "Please Wait for Completion, Depending on the number of files selected this may take some time.",
|
||||
"flv": "flv",
|
||||
"FLV": "FLV",
|
||||
"FLV Stream Type": "FLV Stream Type",
|
||||
|
@ -840,10 +916,14 @@
|
|||
"Confidence":"Confidence",
|
||||
"Trainer Engine":"Trainer Engine",
|
||||
"Train":"Train",
|
||||
"TrainConfirm":"Are you sure you want to begin training? This can take more than 12 hours with over 500 images. This will consume a large amount of resources, like RAM or CPU.",
|
||||
"openImagesDownloadConfirm":"Are you sure you want to begin download images and bounding boxes (preset Matrices) from OpenImages?",
|
||||
"openImagesDownloadConfirmStop":"Are you sure you want to stop training?",
|
||||
"TrainConfirm":"Are you sure you want to begin training? This can take more than 12 hours with over 500 images. This will consume a large amount of resources, like RAM and/or CPU.",
|
||||
"TrainConfirmStop":"Are you sure you want to stop training?",
|
||||
"Batch":"Batch",
|
||||
"Subdivision":"Subdivision",
|
||||
"Map":"Map",
|
||||
"Delay for Snapshot":"Delay for Snapshot",
|
||||
"Add Map":"Add Map",
|
||||
"Add Input Feed":"Add Input Feed",
|
||||
"Add Channel":"Add Channel",
|
||||
|
@ -862,8 +942,10 @@
|
|||
"Show Regions of Interest":"Show Regions of Interest",
|
||||
"Confidence of Detection":"Confidence of Detection",
|
||||
"Edit Selected":"Edit Selected",
|
||||
"Copy Stream Channels":"Copy Stream Channels",
|
||||
"Copy Settings":"Copy Settings",
|
||||
"Copy to Settings":"Copy to Settings",
|
||||
"Copy Mode":"Copy Mode",
|
||||
"Copy Group Settings":"Copy Group Settings",
|
||||
"Copy Connection Settings":"Copy Connection Settings",
|
||||
"Copy Custom Settings":"Copy Custom Settings",
|
||||
|
@ -875,10 +957,13 @@
|
|||
"Copy Recording Settings":"Copy Recording Settings",
|
||||
"Copy Detector Settings":"Copy Detector Settings",
|
||||
"Monitors to Copy to":"Monitors to Copy to",
|
||||
"No Sound": "No Sound",
|
||||
"Notification Sound":"Notification Sound",
|
||||
"Alert Sound":"Alert Sound",
|
||||
"Alert Sound Delay":"Alert Sound Delay",
|
||||
"powerVideoEventLimit":"You have set a high event limit. Are you sure you want to make this request?",
|
||||
"There are no monitors that you can view with this account.":"There are no monitors that you can view with this account.",
|
||||
"Delete Monitors and Files": "Delete Monitors and Files",
|
||||
"Select atleast one monitor to delete": "Select atleast one monitor to delete.",
|
||||
"Use Built-In":"Use Built-In"
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"Add Input Feed": "Ajouter un flux d'entrée",
|
||||
"Add Map": "Ajouter La Carte",
|
||||
"Add Monitor": "Ajouter Moniteur",
|
||||
"Add New": "Ajouter Nouveau",
|
||||
"Add New": "Ajouter De Nouveaux",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avancé",
|
||||
"Again": "Encore",
|
||||
|
@ -131,7 +131,7 @@
|
|||
"DB Lost.. Retrying..": "Base De Données Perdues.. Réessayer..",
|
||||
"DESC": "DESC",
|
||||
"Dashboard": "Tableau de bord",
|
||||
"Dashboard Language": "Langue du Tableau De Bord",
|
||||
"Dashboard Language": "Langue Du Tableau De Bord",
|
||||
"Dashcam": "Dashcam",
|
||||
"Dashcam (Streamer v2)": "Dashcam (Streamer v2)",
|
||||
"Date Range": "Plage De Dates",
|
||||
|
|
906
languages/pt.json
Normal file
906
languages/pt.json
Normal file
|
@ -0,0 +1,906 @@
|
|||
{
|
||||
"\"No Motion\" Detector": "\"Sem Movimento\" Detector De",
|
||||
"# of Allow MJPEG Clients": "# of Allow MJPEG Clients <small>0 for infinite</small>",
|
||||
"180 Degrees": "180 Graus",
|
||||
"2-Factor Authentication": "2-Fator De Autenticação",
|
||||
"90 Clockwise": "90 no sentido Horário",
|
||||
"90 Clockwise and Vertical Flip": "90 no sentido Horário e Vertical Flip",
|
||||
"90 Counter Clockwise and Vertical Flip (default)": "90 no sentido anti-Horário e Vertical (predefinição)",
|
||||
"AND": "E",
|
||||
"API": "API",
|
||||
"API Key": "Chave de API",
|
||||
"API Key Added": "Chave de API Adicionados",
|
||||
"API Key Deleted": "Chave de API Excluídos",
|
||||
"API Keys": "Chaves de API",
|
||||
"APIKeyAddedText": "Você pode usar essa chave agora.",
|
||||
"APIKeyDeletedText": "Chave foi eliminado. Ele não funcionará mais.",
|
||||
"ASC": "ASC",
|
||||
"Accelerator": "Acelerador",
|
||||
"Account Info": "Informações Da Conta",
|
||||
"AccountEditText1": "Não pode editar. Atualizar a página se o problema continua.",
|
||||
"Accounts": "Contas",
|
||||
"Action for Selected": "Ação Selecionado",
|
||||
"Add": "Adicionar",
|
||||
"Add Channel": "Adicionar Canal",
|
||||
"Add Input Feed": "Adicionar Entrada De Alimentação",
|
||||
"Add Map": "Adicionar O Mapa",
|
||||
"Add Monitor": "Adicionar Monitor",
|
||||
"Add New": "Adicionar Novo",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avançado",
|
||||
"Again": "Novamente",
|
||||
"Age": "Idade",
|
||||
"Alert Sound": "Som De Alerta",
|
||||
"Alert Sound Delay": "Som De Alerta Para Atraso",
|
||||
"All Logs": "Todos Os Registos",
|
||||
"All Monitors": "Todos Os Monitores",
|
||||
"All Monitors and Privileges": "Todos os Monitores e Privilégios",
|
||||
"All Privileges": "Todos Os Privilégios",
|
||||
"All Warnings": "Todos Os Avisos",
|
||||
"All streams in first feed": "Todos os fluxos no primeiro feed",
|
||||
"Allow Next Command": "Permitir Seguinte Comando <small>em Minutos</small>",
|
||||
"Allow Next Discord Alert": "Permitir Junto a Discórdia Alerta <small>em Minutos</small>",
|
||||
"Allow Next Email": "Permitir o Próximo e-Mail <small>em Minutos</small>",
|
||||
"Allow Next Trigger": "Permitir Próximo Trigger <small>em Milissegundos</small>",
|
||||
"Allowed IPs": "IPs Permitidos",
|
||||
"Already exists": "Já existe",
|
||||
"Amazon S3": "O Amazon S3",
|
||||
"Amazon S3 Upload Error": "O Amazon S3 Erro De Upload",
|
||||
"Analyzation Duration": "Analyzation Duração",
|
||||
"Archive": "Arquivo",
|
||||
"Are you sure?": "Tem a certeza?",
|
||||
"Attach Video Clip": "Anexar Vídeo Clip",
|
||||
"Audio": "Áudio",
|
||||
"Audio Bit Rate": "Taxa De Bits De Áudio",
|
||||
"Audio Codec": "Codec De Áudio",
|
||||
"Audio Detection": "Detecção De Áudio",
|
||||
"Audio Detector": "Detector De Áudio",
|
||||
"Audio stream only from first feed": "Fluxo de áudio somente a partir de primeiro de alimentação",
|
||||
"Audio streams only": "Fluxos de áudio apenas",
|
||||
"Authenticate": "Autenticar",
|
||||
"Authentication Failed": "Falha De Autenticação",
|
||||
"Auto": "Auto",
|
||||
"Automatic": "Automática",
|
||||
"Autosave": "Autosave",
|
||||
"Backblaze B2": "Backblaze B2",
|
||||
"Backblaze Error": "Backblaze Erro",
|
||||
"Base64 over Websocket": "Base64 mais de Websocket",
|
||||
"Basic Authentication": "Autenticação Básica",
|
||||
"Batch": "Lote",
|
||||
"Bind Credentials": "Vincular Credenciais (Palavra-Passe)",
|
||||
"Blank for No Change": "Em branco para Não Alterar",
|
||||
"Bottom Left": "Inferior Esquerdo",
|
||||
"Bottom Right": "Inferior Direito",
|
||||
"Browser Console Log": "Navegador De Log Do Console",
|
||||
"Bucket": "Balde",
|
||||
"Buffer Preview": "Buffer De Pré-Visualização",
|
||||
"Build": "Construir",
|
||||
"Building": "Construção",
|
||||
"CPU": "CPU",
|
||||
"CPU indicator will not work. Continuing...": "CPU indicador não funciona. Continuando...",
|
||||
"CPU used by this stream": "CPU usada por este fluxo",
|
||||
"CSS": "CSS <small>Estilo de seu painel de controle.</small>",
|
||||
"Calendar": "Calendário",
|
||||
"Call Method": "Método De Chamada",
|
||||
"Camera": "Câmara",
|
||||
"Camera Password": "Câmara Senha",
|
||||
"Camera Username": "Câmara De Nome De Usuário",
|
||||
"Camera is not recording": "A câmera não está de gravação",
|
||||
"Camera is not running": "A câmera não está em execução",
|
||||
"Camera is not streaming": "A câmera não é streaming",
|
||||
"CameraNotRecordingText": "As configurações podem ser incompatíveis. Verifique codificadores. Reiniciando...",
|
||||
"Can Authenticate Websocket": "Pode Autenticar Websocket",
|
||||
"Can Change User Settings": "Pode Alterar As Definições De Utilizador",
|
||||
"Can Control Monitors": "Pode Controlar Monitores",
|
||||
"Can Create and Delete Monitors": "Pode Criar e Excluir Monitores",
|
||||
"Can Delete Videos": "Pode Excluir Vídeos",
|
||||
"Can Delete Videos and Events": "Pode Excluir Vídeos e Eventos",
|
||||
"Can Edit Monitor": "Pode Editar Monitor",
|
||||
"Can Get Logs": "Pode Obter Logs",
|
||||
"Can Get Monitors": "Pode Obter Monitores",
|
||||
"Can View Logs": "Pode Ver Os Logs",
|
||||
"Can View Monitor": "Pode Visualizar O Monitor De",
|
||||
"Can View Snapshots": "Pode Ver Instantâneos",
|
||||
"Can View Streams": "Pode Visualizar Fluxos De",
|
||||
"Can View Videos": "Pode Visualizar Vídeos",
|
||||
"Can View Videos and Events": "Pode Visualizar Vídeos e Eventos",
|
||||
"Can edit Max Days": "Pode editar Max Dias",
|
||||
"Can edit Max Storage": "Pode editar Armazenamento Máximo",
|
||||
"Can edit how long to keep Events": "Pode editar quanto tempo para manter Eventos",
|
||||
"Can edit how long to keep Logs": "Pode editar quanto tempo manter Logs",
|
||||
"Can use Admin Panel": "Pode utilizar o Painel de Administração",
|
||||
"Can use Amazon S3": "Pode usar o Amazon S3",
|
||||
"Can use Discord Bot": "Pode usar a Discórdia Bot",
|
||||
"Can use LDAP": "Pode usar o LDAP",
|
||||
"Can use SFTP": "Pode usar SFTP",
|
||||
"Can use Wasabi Hot Cloud Storage": "Pode usar Wasabi Quente de Armazenamento em Nuvem",
|
||||
"Can use WebDAV": "Pode usar WebDAV",
|
||||
"Can't Connect": "Não pode Ligar",
|
||||
"Cannot watch a monitor that isn't running.": "Não é possível assistir a um monitor que não está em execução.",
|
||||
"Cards": "Cartões",
|
||||
"Center": "Centro de <small>Endereço de URL</small>",
|
||||
"Channel": "Canal",
|
||||
"Channel ID": "ID do canal",
|
||||
"Chat on Discord": "Bate-papo em Discórdia",
|
||||
"Check": "Seleção",
|
||||
"Check Signal Interval": "Verifique o Sinal de Intervalo <small>em Minutos</small>",
|
||||
"Check for Motion First": "Verifique Primeiro Movimento",
|
||||
"Clear Recorder Process": "Claro Gravador De Processo",
|
||||
"Close": "Fechar",
|
||||
"Closed": "Fechado",
|
||||
"Color Threshold": "Limiar De Cores",
|
||||
"Command": "Comando",
|
||||
"Command on Trigger": "Comando no Gatilho",
|
||||
"Complete Stream URL": "Completa URL de Fluxo",
|
||||
"Confidence": "Confiança",
|
||||
"Confidence of Detection": "A confiança de Detecção de",
|
||||
"Configuration": "Configuração",
|
||||
"Confirm": "Confirmar",
|
||||
"Connected": "Conectado",
|
||||
"Connection": "Conexão",
|
||||
"Connection Type": "Tipo De Ligação",
|
||||
"Connection timed out": "Conexão esgotado",
|
||||
"Contains": "Contém",
|
||||
"Control": "Controle",
|
||||
"Control Error": "Erro De Controle De",
|
||||
"ControlErrorText1": "O controle não está habilitado",
|
||||
"ControlErrorText2": "Verifique sua conexão detalhes. Você pode precisar ponto a URL Base em port 8000 ou 80. Verifique a sua informação de autenticação.",
|
||||
"Controllable": "Controlável",
|
||||
"Copy Connection Settings": "Copiar Configurações De Conexão",
|
||||
"Copy Custom Settings": "Copiar Configurações Personalizadas",
|
||||
"Copy Detector Settings": "Copiar As Configurações Do Detector",
|
||||
"Copy Group Settings": "Copiar Configurações Do Grupo",
|
||||
"Copy Input Settings": "Copiar As Configurações De Entrada De",
|
||||
"Copy JPEG API Settings": "Cópia JPEG API de Configurações",
|
||||
"Copy Logging Settings": "Copiar As Configurações De Log",
|
||||
"Copy Mode": "Modo De Cópia",
|
||||
"Copy Recording Settings": "Copiar Configurações De Gravação",
|
||||
"Copy Settings": "Configurações De Cópia",
|
||||
"Copy Stream Channel Settings": "Copiar Configurações De Canal Stream",
|
||||
"Copy Stream Settings": "Copiar Definições Da Sequência",
|
||||
"Copy to Settings": "Copiar Configurações",
|
||||
"Could not create Bucket.": "Não foi possível criar o Balde.",
|
||||
"Country of Plates": "País de Placas",
|
||||
"Counts of Motion": "As contagens de Movimento",
|
||||
"Create Sub-Accounts at /admin": "Criação de Sub-Contas em /admin",
|
||||
"Creating New Account": "Criar Nova Conta",
|
||||
"Creation Interval": "Intervalo De Criação De",
|
||||
"Current": "Atual",
|
||||
"Currently viewing": "Actualmente a visualizar",
|
||||
"Custom": "Personalizado",
|
||||
"Custom Base URL": "Base personalizada URL <small>Deixe em branco para usar a URL de Host</small>",
|
||||
"Custom Endpoint": "Ponto Final Personalizado",
|
||||
"DB Lost.. Retrying..": "Banco De Dados Perdido.. Repetindo..",
|
||||
"DESC": "DESC",
|
||||
"Dashboard": "Dashboard",
|
||||
"Dashboard Language": "Língua Do Dashboard",
|
||||
"Dashcam": "Dashcam",
|
||||
"Dashcam (Streamer v2)": "Dashcam (Streamer v2)",
|
||||
"Database": "Banco de dados",
|
||||
"Database Not Found": "Banco De Dados Não Encontrado",
|
||||
"Database row does not exist": "Linha de base de dados não existe",
|
||||
"Date": "Data",
|
||||
"Date Range": "Intervalo De Data",
|
||||
"Debug": "Depuração",
|
||||
"Default": "Padrão",
|
||||
"Delete": "Apagar",
|
||||
"Delete Filter": "Excluir Filtro",
|
||||
"Delete Matches": "Excluir Partidas",
|
||||
"Delete Monitor": "Excluir Monitor",
|
||||
"Delete Monitor State?": "Apagar O Monitor De Estado",
|
||||
"Delete Monitor States Preset": "Apagar O Monitor De Estados Predefinidos",
|
||||
"Delete Motionless Video": "Excluir Imóvel Vídeo",
|
||||
"Delete Motionless Videos (Record)": "Excluir Imóvel Vídeos (Record)",
|
||||
"Delete Selected Videos": "Excluir Vídeos Selecionados",
|
||||
"Delete Video": "Excluir O Vídeo",
|
||||
"Delete selected": "Excluir selecionado",
|
||||
"DeleteMonitorText": "Você deseja excluir este produto? Você não pode recuperá-lo. Você pode escolher para a permanência de ficheiros no sistema de ficheiros. Se você escolher para recriar um monitor com o mesmo ID, vídeos e eventos tornam-se visíveis no painel de controle.",
|
||||
"DeleteMonitorsText": "Você deseja excluir esses monitores? Você não pode recuperá-los. Você pode optar por manter os arquivos para essas Identificações no sistema de arquivos. Se você escolher para recriar um monitor com uma das Identificações vídeos e eventos tornam-se visíveis no painel de controle.",
|
||||
"DeleteSelectedVideosMsg": "Você deseja excluir esses vídeos? Você não pode recuperá-los.",
|
||||
"DeleteVideoMsg": "Você deseja excluir este vídeo? Você não pode recuperá-lo.",
|
||||
"Deleted": "Excluídos",
|
||||
"Deleted Schedule Configuration": "Excluído Agenda De Configuração",
|
||||
"Deleted State Configuration": "Excluído O Estado De Configuração",
|
||||
"Detect Objects": "Detectar Objectos <small class=\"\">Veja abaixo</small>",
|
||||
"Detection Engine": "O Motor De Detecção De",
|
||||
"Detector": "Detector de",
|
||||
"Detector Buffer": "Detector De Buffer",
|
||||
"Detector Filters": "Detector De Filtros",
|
||||
"Detector Flags": "Detector De Sinalizadores",
|
||||
"Detector Grouping": "Detector de Agrupamento <small>Adicionar grupos de <b>Configurações</b></small>",
|
||||
"Detector Rate": "Detector de Taxa <small>(FPS)</small>",
|
||||
"Detector Recording Complete": "Detector De Gravação Completo",
|
||||
"Detector Recording Process Exited Prematurely. Restarting.": "Detector Processo De Gravação Foi Encerrado Prematuramente. Reiniciar.",
|
||||
"DetectorText": "<p>Quando as caixas Largura e Altura são mostradas, você deve configurá-los para 640x480 ou abaixo. Isto irá otimizar a velocidade de leitura dos quadros.</p>",
|
||||
"Died": "Morreu",
|
||||
"Digest Authentication": "A Autenticação Digest",
|
||||
"Disable Night Vision": "Desative a Visão Noturna <small>Endereço de URL</small>",
|
||||
"Disable Nightvision": "Desativar Nightvision",
|
||||
"Disabled": "Deficiência",
|
||||
"Discord": "Discórdia",
|
||||
"Discord Alert on Trigger": "A discórdia Alerta no Gatilho",
|
||||
"Discord Bot": "A Discórdia Bot",
|
||||
"DiscordErrorText": "Enviar a Discórdia causou um Erro",
|
||||
"DiscordFailedText": "Enviar a Discórdia Falha",
|
||||
"DiscordLoggedIn": "A Discórdia Bot Autenticado",
|
||||
"DiscordNotEnabledText": "A discórdia Bot Não Habilitada, Habilite-o em suas Configurações de Conta.",
|
||||
"Documentation": "Documentação",
|
||||
"Does Not Contain": "Não Contém",
|
||||
"Don't show this anymore": "Não mostrar mais",
|
||||
"Double Quote Directory": "Aspas duplas Diretório de <small>Alguns diretórios espaços. Usando este pode falhar algumas câmeras.</small>",
|
||||
"Down": "Para baixo <small>Endereço de URL</small>",
|
||||
"Down Stop": "Baixo Parar <small>Endereço de URL</small>",
|
||||
"Download": "Baixar",
|
||||
"EU": "UE",
|
||||
"Edit": "Editar",
|
||||
"Edit Selected": "Edit Selected",
|
||||
"Edited Schedule Configuration": "Editado Agenda De Configuração",
|
||||
"Edited State Configuration": "Editado Estado De Configuração",
|
||||
"Email": "E-mail",
|
||||
"Email Details": "Detalhes De E-Mail",
|
||||
"Email address is in use.": "Endereço de e-mail está em uso.",
|
||||
"Email and Password fields cannot be empty": "E-mail e campos de palavra-Passe não pode ser vazio",
|
||||
"Email on No Motion": "E-mail em \"Sem Movimento\"",
|
||||
"Email on Trigger": "E-mail no Gatilho <small>e-Mails ir para o principal titular da conta de início de sessão de endereço.</small>",
|
||||
"Emotion": "Emoção",
|
||||
"Emotion Average": "Emoção Média",
|
||||
"Enable": "Activar",
|
||||
"Enable Night Vision": "Permitem a Visão Noturna <small>Endereço de URL</small>",
|
||||
"Enable Nightvision": "Activar Nightvision",
|
||||
"Enabled": "Habilitado",
|
||||
"End": "Fim",
|
||||
"End Time": "Fim Do Horário De",
|
||||
"Ended": "Terminou",
|
||||
"Endpoint": "Ponto final",
|
||||
"Endpoint Address": "Endereço De Ponto De Extremidade",
|
||||
"Enlarge": "Aumentar",
|
||||
"Enter this code to proceed": "Digite este código para prosseguir",
|
||||
"Equal to": "Igual a",
|
||||
"Error Connecting": "Erro De Conexão",
|
||||
"Error While Decoding": "Erro Durante A Decodificação",
|
||||
"ErrorWhileDecodingText": "Seu hardware pode ter uma conexão instável para a rede. Verifique suas conexões de rede.",
|
||||
"Event": "Eventos",
|
||||
"Event Limit": "Limite De Evento",
|
||||
"Event Webhook Error": "Evento Webhook Erro",
|
||||
"EventText1": "Desencadeou um movimento de evento em",
|
||||
"EventText2": "Não poderia de e-mail de imagem, o arquivo não estava acessível",
|
||||
"Events": "Eventos",
|
||||
"Example": "Exemplo",
|
||||
"Execute Command": "Executar O Comando",
|
||||
"Executed": "Executado",
|
||||
"Export": "Exportação",
|
||||
"Export Selected Videos": "Exportar Vídeos Selecionados",
|
||||
"Export Video": "Exportação De Vídeo",
|
||||
"ExportSelectedVideosMsg": "Você deseja exportar esses vídeos? Pode levar algum tempo para zip e fazer o download.",
|
||||
"FFmpegCantStart": "O FFmpeg não Poderia Iniciar",
|
||||
"FFmpegCantStartText": "O motor de gravação para esta câmara pode não iniciar. Há algo de errado com sua configuração da câmera. Se existem quaisquer registos que não este, por favor, poste-os em <b>Problemas</b> no Github.",
|
||||
"FFmpegTip": "FFprobe é um simples fluxos multimídia analyzer. Você pode usá-lo para a saída de todos os tipos de informações sobre uma entrada, incluindo duração, taxa de quadros, tamanho do quadro, etc.",
|
||||
"FFprobe": "Sonda",
|
||||
"FLV": "FLV",
|
||||
"FLV Stream Type": "FLV Tipo de Fluxo",
|
||||
"FactorAuthText1": "O código só estará ativa por 15 minutos. Se você fizer login novamente o temporizador será redefinida para 15 minutos com o mesmo código.",
|
||||
"Fatal": "Fatal",
|
||||
"Fatal Maximum Reached": "Fatal Máximo Atingido, Parando Câmara.",
|
||||
"FatalMaximumReachedText": "JPEG Erro foi fatal.",
|
||||
"Feed-in Image Height": "Alimentação-na Altura da Imagem",
|
||||
"Feed-in Image Width": "Alimentação-na Largura da Imagem",
|
||||
"Female": "Feminino",
|
||||
"Fields cannot be empty": "Campos não pode ser vazio",
|
||||
"File Delete Error": "Excluir O Arquivo De Erro",
|
||||
"File Not Exist": "Arquivo Não Existe",
|
||||
"File Not Found": "Arquivo Não Encontrado",
|
||||
"File Not Found in Database": "Arquivo Não Encontrado no Banco de dados",
|
||||
"File Not Found in Filesystem": "Arquivo Não Encontrado no sistema de arquivos",
|
||||
"File Type": "Tipo De Arquivo",
|
||||
"FileNotExistText": "Não é possível guardar não existir arquivo. Alguma coisa saiu errada.",
|
||||
"Filename": "Nome de ficheiro",
|
||||
"Filesize": "Tamanho do arquivo",
|
||||
"Filter ID": "ID do filtro",
|
||||
"Filter Matches": "Correspondências De Filtro",
|
||||
"Filter Name": "Nome Do Filtro",
|
||||
"Filter for Objects only": "Filtro para que somente os Objetos",
|
||||
"FilterMatchesText1": "Este filtro tem cumprido as condições.",
|
||||
"FilterMatchesText2": "vídeos encontrados.",
|
||||
"Filters": "Filtros",
|
||||
"Filters Updated": "Filtros Atualizado",
|
||||
"FiltersUpdatedText": "As suas alterações foram salvas e aplicadas.",
|
||||
"Find Where": "Onde Encontrar",
|
||||
"First stream in feed": "Primeiro fluxo de alimentação",
|
||||
"Fix": "Correcção",
|
||||
"Fix Video": "Correcção De Vídeo",
|
||||
"FixVideoMsg": "Você deseja corrigir esse vídeo? Você não pode desfazer esta ação.",
|
||||
"Flush PM2 Logs": "Flush Logs de PM2",
|
||||
"Font Path": "Caminho De Fonte",
|
||||
"Font Size": "Tamanho Da Fonte",
|
||||
"For Group": "Para O Grupo",
|
||||
"Force Monitors Per Row": "Força De Monitores Por Linha",
|
||||
"Force Port": "Força Porto",
|
||||
"Form Data Not Found": "Formulário De Dados Não Encontrado",
|
||||
"Found Devices": "Dispositivos Encontrados",
|
||||
"Frame Rate": "Taxa De Quadros",
|
||||
"Full Frame Detection": "Quadro Completo De Detecção De",
|
||||
"Full Stream URL": "Total URL de Fluxo",
|
||||
"Full URL Path": "Caminho completo da URL",
|
||||
"Fullscreen": "Fullscreen",
|
||||
"Gender": "Sexo",
|
||||
"Generate Subtitles": "Gerar Legendas",
|
||||
"Get Logs to Client": "Obter Logs do Cliente",
|
||||
"Global Detector Settings": "Global As Configurações Do Detector",
|
||||
"Greater Than": "Maior Que",
|
||||
"Greater Than or Equal to": "Maior Que ou Igual a",
|
||||
"Group Key": "Chave De Grupo",
|
||||
"Group Key is in use.": "Chave de grupo está em uso.",
|
||||
"Group Name": "Nome Do Grupo",
|
||||
"Grouping": "Agrupamento de",
|
||||
"H.264 / H.265 / H.265+": "H. 264 / H. 265 / H. 265 ",
|
||||
"HEVC (H.265)": "HEVC (H. 265)",
|
||||
"HLS (.m3u8)": "HLS (.m3u8)",
|
||||
"HLS (includes Audio)": "HLS (inclui Áudio)",
|
||||
"HLS Audio Encoder": "Codificador De Áudio",
|
||||
"HLS List Size": "O Tamanho Da Lista",
|
||||
"HLS Live Start Index": "HLS ao Vivo Índice de Início",
|
||||
"HLS Preset": "Modelo Predefinido",
|
||||
"HLS Segment Length": "Comprimento do segmento <small>em Segundos</small>",
|
||||
"HLS Start Number": "HLS Número de Início",
|
||||
"HLS Video Encoder": "Codificador De Vídeo",
|
||||
"HTTP": "HTTP",
|
||||
"HTTPS": "HTTPS",
|
||||
"Hardware Accelerated": "Aceleração De Hardware",
|
||||
"Height": "Altura",
|
||||
"Help": "Ajuda",
|
||||
"Hide List": "Ocultar A Lista",
|
||||
"Hide Notes": "Ocultar Notas",
|
||||
"Host": "Host",
|
||||
"Host Type": "Tipo De Host",
|
||||
"Hotswap Modes (Watch-Only)": "Hotswap Modos (Ver Somente)",
|
||||
"How to Record": "Como Gravar",
|
||||
"IP Address": "Endereço IP",
|
||||
"Identity": "Identidade",
|
||||
"IdentityText1": "Esta é a forma como o sistema irá identificar os dados para este fluxo. Você não pode alterar o <b>ID do Monitor</b> depois de ter pressionado salvar. Se você quiser você pode fazer o <b>Monitor de IDENTIFICAÇÃO</b> mais legível para humanos antes de continuar.",
|
||||
"IdentityText2": "Você pode duplicar um monitor modificando o <b>Monitor de IDENTIFICAÇÃO</b> , em seguida, pressionando salvar. Você <b>não pode</b> usar o ID de um monitor que já existe, ou ele vai economizar mais que o monitor de informações do banco de dados.",
|
||||
"Idle": "Ocioso",
|
||||
"Image Height": "Altura Da Imagem",
|
||||
"Image Location": "Localização da imagem de <small>Caminho Absoluto ou deixe em branco para usar o global</small>",
|
||||
"Image Position": "Posição Da Imagem",
|
||||
"Image Width": "Largura Da Imagem",
|
||||
"Import": "Importação",
|
||||
"Import Monitor Configuration": "Importação De Configuração Do Monitor De",
|
||||
"ImportMonitorConfigurationText": "Fazendo isso vai overrwrite quaisquer alterações no momento não salvos. Importado alterações só serão aplicadas quando você pressionar <b>Salvar</b>.",
|
||||
"ImportMultiMonitorConfigurationText": "Fazendo isso vai overrwrite qualquer monitores com IDs existentes no arquivo de importação.",
|
||||
"In": "No",
|
||||
"Incorrect Settings Chosen": "Configurações Incorretas Escolhido",
|
||||
"Indifference": "A indiferença",
|
||||
"Input": "Entrada",
|
||||
"Input Feed": "Entrada De Alimentação",
|
||||
"Input Flags": "A Entrada De Sinalizadores",
|
||||
"Input Selector": "Selector De Entrada",
|
||||
"Input Settings": "Configurações De Entrada De",
|
||||
"Input Type": "Tipo De Entrada",
|
||||
"InputText1": "Esta secção diz Shinobi como consumir um fluxo. Para melhor desempenho, tente afinar a sua câmera configurações internas. Encontrar as seguintes opções e defina-as como mostrado. Para encontrar a sua câmera, você pode usar o <b>construída em ONVIF Scanner</b> de Shinobi. Alguns ONVIF câmeras exigem o uso de uma ferramenta de gestão para modificar suas configurações internas. Se você não pode encontrar câmeras, você pode tentar <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">ONVIF Gerenciador de Dispositivos do Windows</a>.",
|
||||
"InputText2": "<ul><li><b>A taxa de quadros (FPS) :</b> Alta : de 10 a 15 FPS, Baixa : de 2 a 5 FPS</li><li><b>I-intervalo :</b> 1</li><li><b>A Taxa de bits do Tipo :</b> VBR (Taxa de Bit Variável)</li><li><b>Taxa de bits :</b> entre 256kbps - 1000kbps</li></ul>",
|
||||
"InputText3": "Se você precisar de ajuda para descobrir que tipo de entrada de sua câmera é que você pode dar uma olhada na <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Câmera Lista de URLs</a> no Shinobi site.",
|
||||
"Inserted Schedule Configuration": "Inserido Agenda De Configuração",
|
||||
"Inserted State Configuration": "Inserido Estado De Configuração",
|
||||
"Invalid Data": "Dados Inválidos",
|
||||
"Invalid JSON": "Inválido JSON",
|
||||
"InvalidJSONText": "Por favor, garantir que este é válido seqüência de caracteres JSON para Shinobi de configuração do monitor.",
|
||||
"JPEG": "JPEG",
|
||||
"JPEG (Auto Enables JPEG API)": "JPEG (Auto Permite JPEG API)",
|
||||
"JPEG API": "JPEG API",
|
||||
"JPEG Error": "JPEG Erro",
|
||||
"JPEG Mode": "Modo JPEG",
|
||||
"JPEGErrorText": "Havia um problema a obtenção de dados a partir da sua câmara.",
|
||||
"LDAP": "LDAP",
|
||||
"LDAP Success": "LDAP Sucesso",
|
||||
"LDAP User Authenticated": "LDAP Usuário Autenticado",
|
||||
"LDAP User is New": "Usuário LDAP é Novo",
|
||||
"Launch in New Window": "O lançamento na Nova Janela",
|
||||
"Leave blank for random.": "Deixe em branco para aleatórios.",
|
||||
"Leave blank for unlimited": "Deixe em branco para ilimitado",
|
||||
"Left": "Esquerda <small>Endereço de URL</small>",
|
||||
"Left Stop": "Batente esquerdo <small>Endereço de URL</small>",
|
||||
"Less Than": "Menos De",
|
||||
"Less Than or Equal to": "Menor ou Igual a",
|
||||
"License Plate Detector": "Placa De Licença Detector De",
|
||||
"Like": "Como",
|
||||
"Limited": "Limitado",
|
||||
"Link Shinobi": "Link Shinobi",
|
||||
"List Toggle": "Lista De Alternância",
|
||||
"List of Videos Delete Error": "Lista de Vídeos Apagar Erro",
|
||||
"Live Stream Toggle": "Transmissão Ao Vivo De Alternância",
|
||||
"Live View": "Visualização Ao Vivo",
|
||||
"Local": "Local",
|
||||
"Log Level": "O Nível De Log De",
|
||||
"Log Signal Event": "Log de Evento de Sinal <small>apenas do lado do Cliente</small>",
|
||||
"Log Stream": "Fluxo De Log",
|
||||
"Logging": "Registo",
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"Logs": "Logs",
|
||||
"Loop Stream": "Loop Stream",
|
||||
"MB": "MB",
|
||||
"MJPEG": "MJPEG",
|
||||
"MP4 (copy, libx264, libx265)": "MP4 (copiar, libx264, libx265)",
|
||||
"MPEG-4 (.mp4 / .ts)": "MPEG-4 (.mp4 / .ts)",
|
||||
"MPEG-DASH (includes Audio)": "MPEG-DASH (inclui Áudio)",
|
||||
"MailError": "O CORREIO ERRO : não foi possível enviar o e-mail, Verifique conf.json. Ignorando quaisquer características de depender de discussão.",
|
||||
"Main": "Principal",
|
||||
"Male": "Masculino",
|
||||
"Manual": "Manual",
|
||||
"Map": "Mapa",
|
||||
"Matches": "Jogos",
|
||||
"Max Indifference": "Max Indiferença",
|
||||
"Max Latency": "Max Latência",
|
||||
"Max Number of Cameras": "Número máx de Câmaras",
|
||||
"Max Storage Amount": "Max Quantidade De Armazenamento",
|
||||
"Maximum dB": "Máximo dB",
|
||||
"Merge Selected Videos": "Mesclar Vídeos Selecionados",
|
||||
"Merge Video": "Vídeo De Impressão",
|
||||
"Merge and Download": "De mala e fazer o Download",
|
||||
"MergeSelectedVideosMsg": "Você deseja mesclar esses vídeos? Pode levar algum tempo para mesclar e fazer o download. O momento em que a conexão é fechada, o arquivo será excluído. Certifique-se de manter o navegador aberto até que seja concluída.",
|
||||
"Migrator": "Migrator",
|
||||
"Minimum dB": "Mínimo dB",
|
||||
"Mode": "Modo de",
|
||||
"Monitor": "Monitor",
|
||||
"Monitor Added by user": "Monitor Adicionado pelo usuário.",
|
||||
"Monitor Capture Rate": "Monitor de Taxa de Captura <small>(FPS)</small>",
|
||||
"Monitor Groups": "Monitor De Grupos De",
|
||||
"Monitor ID": "ID do Monitor",
|
||||
"Monitor Idling": "Monitor De Marcha Lenta",
|
||||
"Monitor Name": "Nome Do Monitor",
|
||||
"Monitor Settings": "Configurações Do Monitor",
|
||||
"Monitor States": "Monitor De Estados",
|
||||
"Monitor States and Schedules": "Monitor de Estados e Horários",
|
||||
"Monitor Stopped": "Monitor Parou",
|
||||
"Monitor Updated by user": "Monitor Atualizados pelo usuário.",
|
||||
"Monitor is now Disabled": "O Monitor é Desativado",
|
||||
"Monitor is now Idle": "O Monitor está agora Inactiva",
|
||||
"Monitor is now Recording": "O Monitor é agora de Gravação",
|
||||
"Monitor is now Watching": "O Monitor está agora Assistindo",
|
||||
"Monitor mode changed": "O Monitor de modo alterado",
|
||||
"Monitor mode is already": "O modo Monitor é já",
|
||||
"Monitor or Key does not exist.": "Monitor ou a Chave não existe.",
|
||||
"MonitorIdlingText": "Sessão de Monitor tem sido ordenado ocioso.",
|
||||
"MonitorStoppedText": "Monitor de sessão, foi obrigado a parar.",
|
||||
"Monitors": "Monitores",
|
||||
"Monitors per row": "Monitores por linha <small>de Montagem</small>",
|
||||
"Monitors to Copy to": "Monitores para Copiar para",
|
||||
"Montage": "Montagem",
|
||||
"Motion": "Movimento",
|
||||
"Motion Detection": "Detecção De Movimento",
|
||||
"Motion GUI": "Movimento GUI",
|
||||
"Motion Meter": "Medidor De Movimento",
|
||||
"Mp4Frag": "Mp4Frag",
|
||||
"Must be atleast one row": "Deve ser pelo menos uma linha",
|
||||
"Mute Audio": "Silenciar Áudio",
|
||||
"NVIDIA": "NVIDIA",
|
||||
"Name": "Nome",
|
||||
"Name cannot be empty.": "O nome não pode estar vazia.",
|
||||
"New Authentication Token": "Novo Token De Autenticação",
|
||||
"New Monitor": "Novo Monitor",
|
||||
"No": "Nenhum",
|
||||
"No Audio": "Sem Áudio",
|
||||
"No Data": "Não Há Dados",
|
||||
"No Events found for this video": "Nenhum Eventos encontrado para esse vídeo",
|
||||
"No Group with this key exists": "Nenhum Grupo com esta chave existir",
|
||||
"No Monitor Exists with this ID.": "O Monitor não Existe com este ID.",
|
||||
"No Monitor Found, Ignoring Request": "O Monitor Não Encontrado, Ignorando O Pedido De",
|
||||
"No Monitor ID Present in Form": "Nenhum ID do Monitor Presente no Formulário",
|
||||
"No Monitors Selected": "Não Há Monitores Selecionados",
|
||||
"No Rotation": "Sem Rotação",
|
||||
"No Videos Found": "Nenhum Vídeo Encontrado",
|
||||
"No such file": "Não existe tal arquivo",
|
||||
"NoLogsFoundForDateRange": "Sem Registos encontrados neste intervalo de data. Tente alargar o intervalo de data.",
|
||||
"NoMotionEmailText1": "Nenhum Movimento para",
|
||||
"NoMotionEmailText2": "Não houve nenhuma detecção de movimento de câmera para",
|
||||
"NoVideosFoundForDateRange": "Nenhum vídeo encontrado neste intervalo de data. Tente definir a data de início mais para trás.",
|
||||
"Noise Filter": "Filtro De Ruído",
|
||||
"Noise Filter Range": "Filtro De Ruído Faixa De",
|
||||
"Not Authorized": "Não Autorizado",
|
||||
"Not Connected": "Não Conectado",
|
||||
"Not Equal to": "Não Igual a",
|
||||
"Not In": "Não",
|
||||
"Not Matches": "Não Corresponde",
|
||||
"Not Permitted": "Não Permitido",
|
||||
"Not Saved": "Não Salvo",
|
||||
"Not an Administrator Account": "Não é uma Conta de Administrador",
|
||||
"NotAuthorizedText1": "Não Autorizado, Enviar comando init com \"auth\",\"ke\", \"uid\"",
|
||||
"Notes": "Notas",
|
||||
"NotesPlacholder": "Os comentários que você quer deixar para esta câmeras de configurações.",
|
||||
"Nothing exists": "Nada existe",
|
||||
"Notification Sound": "Som De Notificação",
|
||||
"Notification Video Length": "Notificação De Duração Do Vídeo",
|
||||
"Number of Days to keep": "Número de Dias para manter",
|
||||
"ONVIF": "ONVIF",
|
||||
"ONVIF Port": "ONVIF Porta",
|
||||
"ONVIF Scanner": "ONVIF Scanner",
|
||||
"ONVIFEventsNotAvailable": "ONVIF Eventos não Disponível",
|
||||
"ONVIFnotCompliantProfileT": "A câmera não está ONVIF Perfil T Complacente",
|
||||
"ONVIFnote": "Descubra ONVIF dispositivos em redes fora do seu próprio ou deixe em branco para digitalizar sua rede atual. <br>Nome de utilizador e palavra-Passe pode ser deixado em branco.",
|
||||
"OR": "OU",
|
||||
"Object": "Objeto",
|
||||
"Object Detection": "A Detecção De Objetos",
|
||||
"Object Tag": "Objeto Tag",
|
||||
"OpenCV Cascades": "OpenCV Cascatas",
|
||||
"Operating Hours": "Horário De Funcionamento",
|
||||
"Options": "Opções",
|
||||
"Order Streams": "A Ordem De Fluxos De",
|
||||
"Output Method": "Método De Saída",
|
||||
"Password": "Palavra-passe",
|
||||
"Password Again": "Senha Novamente",
|
||||
"Passwords don't match": "As senhas não combinam",
|
||||
"Paste JSON here.": "Colar JSON aqui.",
|
||||
"Path": "Caminho",
|
||||
"Permissions": "Permissões",
|
||||
"Ping Failed": "O Ping",
|
||||
"Please Check Your Settings": "Por Favor Verifique As Suas Definições",
|
||||
"Please Wait for Completion": "Por favor, Aguarde a Conclusão, Dependendo do número de arquivos selecionados isso pode levar algum tempo. Atualizar para verificar novamente.",
|
||||
"Plugin": "Plugin",
|
||||
"Points": "Pontos <small>Quando a adição de pontos, clique na borda do polígono.</small>",
|
||||
"Pop": "Pop",
|
||||
"Popout Monitor on Event": "Popout Monitor de Eventos",
|
||||
"Port": "Porta",
|
||||
"Pose": "Pose",
|
||||
"Poseidon": "Poseidon",
|
||||
"Position X": "Posição X",
|
||||
"Position Y": "Posição Y",
|
||||
"Power Video Viewer": "O Poder Do Visualizador De Vídeo",
|
||||
"Power Viewer": "Poder Viewer",
|
||||
"Preferences": "Preferências",
|
||||
"Preset": "Preset",
|
||||
"Presets": "Predefinições",
|
||||
"Preview": "Pré-visualização",
|
||||
"Primary Engine": "Motor Principal",
|
||||
"Primary Input": "Entrada Principal",
|
||||
"Privileges": "Privilégios",
|
||||
"Probe Size": "Sonda Tamanho",
|
||||
"Process Crashed for Monitor": "Processo Caiu para Monitor",
|
||||
"Process Started": "Processo Iniciado",
|
||||
"Process Unexpected Exit": "Processo De Saída Inesperado",
|
||||
"Processor": "Processador",
|
||||
"Profile": "Perfil",
|
||||
"Quality": "Qualidade <small>1 é Elevado, 23 de Baixo</small>",
|
||||
"Query": "Consulta",
|
||||
"Quick Sync Video": "Quick Sync Video",
|
||||
"RAM": "RAM",
|
||||
"RTMP": "RTMP",
|
||||
"RTMP Stream": "RTMP Stream",
|
||||
"RTMP Stream Flags": "RTMP Fluxo de Sinalizadores",
|
||||
"RTMPS": "RTMPS",
|
||||
"RTSP": "RTSP",
|
||||
"RTSP Transport": "RTSP Transporte",
|
||||
"Range or Single": "Intervalo ou Único",
|
||||
"Raspberry Pi": "Raspberry Pi",
|
||||
"Rate": "Taxa <small>(FPS)</small>",
|
||||
"Raw": "Matérias",
|
||||
"Raw H.264 Stream": "Matérias H. 264 Stream",
|
||||
"Reason": "Razão",
|
||||
"Recent Videos": "Vídeos Recentes",
|
||||
"Recommended": "Recomendado",
|
||||
"Record": "Registro",
|
||||
"Record File Type": "Registro De Tipo De Arquivo",
|
||||
"Record Height": "Altura Do Registo",
|
||||
"Record Video Filter": "Registro De Filtro De Vídeo",
|
||||
"Record Width": "Registro De Largura",
|
||||
"Recorded Buffer": "Gravado Buffer",
|
||||
"Recording": "Gravação",
|
||||
"Recording FPS": "Gravação de FPS",
|
||||
"Recording FPS Change on Start": "Gravação de FPS Alterar em Iniciar",
|
||||
"Recording Flags": "Gravação De Sinalizadores",
|
||||
"Recording Segment Interval": "Gravação Segmento de Intervalo <small>em minutos</small>",
|
||||
"Recording Timeout": "Gravação de tempo limite <small>em Minutos</small>",
|
||||
"Recording Timestamp": "Gravação De Carimbo De Data / Hora",
|
||||
"Recording Watermark": "Gravação Com Marca De Água",
|
||||
"RecordingText": "É recomendável que você defina o <b>Registro de Tipo de Arquivo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">, MP4</b> e <b>Codec de Vídeo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copiar ou </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> porque o <b>Tipo de Entrada</b> é definida como <b class=\"h_t_text\"></b>.",
|
||||
"Refresh List of Cascades": "Atualizar Lista de Cascatas",
|
||||
"Region": "Região",
|
||||
"Region Editor": "Região Do Editor",
|
||||
"Region Name": "Nome Da Região",
|
||||
"RegionNote": "A pontuação só será salvo quando você pressionar <b>Salvar</b> no <b>Monitor Configurações de</b> janela.",
|
||||
"Regions": "Regiões",
|
||||
"Remember Me": "Lembrar-Me",
|
||||
"Request": "Pedido",
|
||||
"Require Object to be in Region": "Exigir Objeto a ser na Região",
|
||||
"Reset Timer": "Repor O Temporizador",
|
||||
"Restart": "Reinicie",
|
||||
"Restart CRON": "Reinicie o CRON",
|
||||
"Restart Core": "Reinicie O Núcleo",
|
||||
"Restarting": "Reiniciar",
|
||||
"Restarting Process": "Reiniciar O Processo De",
|
||||
"Retry Connection": "Repetição de Conexão <small>Número de vezes permitido falhar</small>",
|
||||
"Retrying...": "Repetindo...",
|
||||
"Right": "Direito <small>de Endereço de URL</small>",
|
||||
"Right Stop": "Direito de Interromper <small>Endereço de URL</small>",
|
||||
"Rotate": "Girar",
|
||||
"S3-Based Network Storage": "S3-Armazenamento Em Rede",
|
||||
"SFTP": "SFTP",
|
||||
"SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)",
|
||||
"SFTP Error": "SFTP Erro",
|
||||
"Save": "Salvar",
|
||||
"Save Directory": "Salvar Diretório",
|
||||
"Save Events to SQL": "Salvar Eventos SQL",
|
||||
"Save Frames to Events": "Salvar Quadros para Eventos",
|
||||
"Save Links to Database": "Salvar Links para Banco de dados",
|
||||
"Save Log in SQL": "Salvar Log no SQL <small>Isto pode encher-se rapidamente.</small>",
|
||||
"Save as": "Salvar como",
|
||||
"Saved Filters": "Filtros Salvos",
|
||||
"Saved Presets": "Salvo Presets",
|
||||
"Saved Schedules": "Salvo Horários",
|
||||
"Scan Settings": "Configurações De Digitalização",
|
||||
"Schedule": "Programação",
|
||||
"Schedule Configuration Not Found": "Agenda De Configuração Não Encontrado",
|
||||
"Schedules": "Horários",
|
||||
"Search": "Procura",
|
||||
"Search Base": "Base De Pesquisa",
|
||||
"Search Filter": "Filtro De Pesquisa",
|
||||
"Search Images": "Procurar Imagens",
|
||||
"Second stream in feed": "Segundo fluxo de alimentação",
|
||||
"Secure": "Seguro",
|
||||
"Send Frames": "Enviar Quadros <small>Empurrar quadros a serem analisados</small>",
|
||||
"Separate with commas, no spaces": "Separe-os com vírgulas, sem espaços",
|
||||
"Server URL": "URL do servidor",
|
||||
"Set to Watch Only": "Conjunto de Assistir Somente",
|
||||
"Settings": "Definições",
|
||||
"Settings Changed": "As Configurações Alteradas",
|
||||
"SettingsChangedText": "As configurações foram salvas e aplicadas. Algumas definições podem exigir uma atualização desta página.",
|
||||
"Shinobi": "Shinobi",
|
||||
"Shinobi Streamer": "Shinobi Streamer",
|
||||
"Show Logs": "Mostrar Logs",
|
||||
"Show Matrices": "Mostrar Matrizes",
|
||||
"Show Matrix": "Mostrar A Matriz",
|
||||
"Show Regions of Interest": "Mostrar as Regiões de Interesse",
|
||||
"Show Stream HUD": "Show Stream HUD",
|
||||
"Show Thumbnails in Video List": "Mostrar Miniaturas na Lista de filmes",
|
||||
"Silent": "Silêncio",
|
||||
"Simple": "Simples",
|
||||
"Size (mb)": "Tamanho (mb)",
|
||||
"Skip Ping": "Pular Ping",
|
||||
"Snapshot": "Instantâneo",
|
||||
"Snapshot Flags": "Instantâneo Bandeiras",
|
||||
"Snapshots": "Instantâneos",
|
||||
"Sort By": "Ordenar Por",
|
||||
"Space Used": "Espaço Utilizado",
|
||||
"Start": "Início",
|
||||
"Start Recording": "Iniciar A Gravação",
|
||||
"Start Time": "Hora De Início",
|
||||
"Start Time cannot be empty.": "Horário de início não pode estar vazia.",
|
||||
"Started": "Iniciado",
|
||||
"Started Building": "O Início De Construção",
|
||||
"Starting": "Partida",
|
||||
"State Configuration Not Found": "Estado De Configuração Não Encontrado",
|
||||
"State Configuration has no monitors associated": "Configuração de estado não tem monitores associados",
|
||||
"Status Changed": "Status Alterado",
|
||||
"Status Indicator": "Indicador De Status",
|
||||
"Stop": "Parar",
|
||||
"Stop Command": "Comando De Parada",
|
||||
"Stop URL": "Parar de URL",
|
||||
"Stopped": "Parado",
|
||||
"Storage Location": "Local De Armazenamento",
|
||||
"Stream": "Stream",
|
||||
"Stream Channel": "Canal De Transmissão Em Sequência",
|
||||
"Stream Flags": "Fluxo De Sinalizadores",
|
||||
"Stream Key": "Chave De Fluxo",
|
||||
"Stream Timestamp": "Fluxo De Carimbo De Data / Hora",
|
||||
"Stream Type": "Tipo De Fluxo",
|
||||
"Stream Watermark": "Fluxo De Marca De Água",
|
||||
"Stream to YouTube": "Stream para o YouTube",
|
||||
"Stream to YouTube Flags": "Stream para o YouTube Bandeiras",
|
||||
"StreamText": "<p>Esta seção irá designar o principal método de transmissão e suas configurações. Este fluxo será exibida no painel de controle. Se você escolher usar HLS, JPEG, ou MJPEG, em seguida, você pode consumir o fluxo através de outros programas.</p><p class=\"h_st_input h_st_jpeg\">Usando JPEG fluxo essencialmente desliga o fluxo principal e usa o instantâneo bin para obter quadros.</p>",
|
||||
"Streamer": "Streamer",
|
||||
"Streams": "Fluxos de",
|
||||
"Subdivision": "Subdivisão",
|
||||
"Success": "Sucesso",
|
||||
"Superuser": "Superuser",
|
||||
"Superuser Logs": "Superusuário Logs",
|
||||
"Switch on for Still Image": "Ligue para Imagens fixas",
|
||||
"System": "Sistema",
|
||||
"TCP": "TCP",
|
||||
"TV Channel": "TV Canal",
|
||||
"TV Channel Group": "Canal de TV do Grupo",
|
||||
"TV Channel ID": "TV Canal ID",
|
||||
"Text Box Color": "Cor Da Caixa De Texto",
|
||||
"Text Color": "Cor Do Texto",
|
||||
"Themes": "Temas",
|
||||
"There are no monitors that you can view with this account.": "Não há monitores que você pode ver com esta conta.",
|
||||
"Thumbnail": "Miniatura",
|
||||
"Time": "Horário",
|
||||
"Time Left": "Tempo Restante",
|
||||
"Time Occurred": "Tempo Ocorreu",
|
||||
"Time-lapse": "Time-lapse",
|
||||
"Time-lapse Tool": "Lapso de tempo de Ferramenta",
|
||||
"Timelapse": "Timelapse",
|
||||
"Timeout": "Tempo de espera",
|
||||
"Timeout Reset on Next Event": "Tempo limite de Reset no Próximo Evento",
|
||||
"Timeout Reset on Next Motion": "Tempo limite de Reset no Próximo Movimento",
|
||||
"Toggle Sidebar": "Alternar A Barra Lateral",
|
||||
"Token": "Token",
|
||||
"Top Left": "Parte Superior Esquerda",
|
||||
"Top Right": "Superior Direito",
|
||||
"Traditional (Watch-Only, Includes Buffer)": "Tradicional (Veja Só, Inclui Buffer)",
|
||||
"Traditional Recording": "Tradicional De Gravação",
|
||||
"Traditional Recording Flags": "A Tradicional Gravação De Sinalizadores",
|
||||
"Train": "De trem",
|
||||
"TrainConfirm": "Tem certeza de que deseja para começar a treinar? Isto pode demorar mais de 12 horas, com mais de 500 imagens. Isto irá consumir uma grande quantidade de recursos, como memória RAM ou CPU.",
|
||||
"Trainer Engine": "Treinador Do Motor",
|
||||
"Trigger Camera Groups": "Disparador Da Câmera Grupos",
|
||||
"Trigger Group to Record": "Gatilho do Grupo para Gravar",
|
||||
"Trigger Record": "Gatilho De Registro",
|
||||
"Trigger Successful": "Gatilho De Sucesso",
|
||||
"Trigger Threshold": "Gatilho Limite",
|
||||
"UDP": "UDP",
|
||||
"URL": "URL",
|
||||
"URL Stop Timeout": "URL Parar o tempo limite <small>de Execução de parada URL depois de X milisegundos</small>",
|
||||
"US": "EUA",
|
||||
"Unable to Launch": "Não é possível Iniciar",
|
||||
"UnabletoLaunchText": "Por favor, guardar o novo monitor de primeira. Em seguida, tenta iniciar a região do editor.",
|
||||
"Uniform": "Uniforme",
|
||||
"Up": "Até <small>o Endereço URL</small>",
|
||||
"Up Stop": "Até Parar de <small>Endereço de URL</small>",
|
||||
"Update": "Atualização",
|
||||
"Update to Development": "Actualização para o Desenvolvimento",
|
||||
"Update to Master": "Atualização para o Mestre de",
|
||||
"Upload File": "Upload Do Arquivo",
|
||||
"Uploaders": "Uploaders",
|
||||
"Use Built-In": "Use Built-In",
|
||||
"Use Global Amazon S3 Video Storage": "Uso Global Do Amazon S3 Armazenamento De Vídeo",
|
||||
"Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Armazenamento De Vídeo",
|
||||
"Use Global Wasabi Hot Cloud Storage Video Storage": "Uso Global Do Wasabi Quente De Armazenamento Em Nuvem De Armazenamento De Vídeo",
|
||||
"Use Global WebDAV Video Storage": "Uso Global Do WebDAV Armazenamento De Vídeo",
|
||||
"Use HTML5 Play Method": "Utilizar o HTML5 Método Play",
|
||||
"Use Max Storage Amount": "Usar O Max Quantidade De Armazenamento",
|
||||
"Use coProcessor": "O uso de co-processador",
|
||||
"User Not Found": "Usuário Não Encontrado",
|
||||
"Username": "Nome de usuário",
|
||||
"VA-API": "VA-API",
|
||||
"Value": "Valor",
|
||||
"Video": "Vídeo",
|
||||
"Video Bit Rate": "Taxa De Bits De Vídeo",
|
||||
"Video Codec": "Codec De Vídeo",
|
||||
"Video Filter": "Filtro De Vídeo",
|
||||
"Video Finished": "Vídeo Terminar",
|
||||
"Video Length (minutes) and Motion Count per video": "Duração do vídeo (minutos) e o Movimento de Contagem por vídeo",
|
||||
"Video Limit": "Limite De Vídeo",
|
||||
"Video Record Rate": "Gravação de vídeo Taxa de <small>(FPS)</small>",
|
||||
"Video Set": "Conjunto De Vídeo",
|
||||
"Video Status": "De Estado Do Vídeo",
|
||||
"Video and Time Span (Minutes)": "O vídeo e o Tempo de duração (Minutos)",
|
||||
"Video stream only from first feed": "Fluxo de vídeo somente a partir de primeiro de alimentação",
|
||||
"Video streams only": "Fluxos de vídeo informativo",
|
||||
"Videos": "Vídeos",
|
||||
"Videos List": "Lista De Vídeos",
|
||||
"Videos Merge": "Vídeos De Mesclagem",
|
||||
"Warning": "Aviso",
|
||||
"Wasabi Hot Cloud Storage": "Wasabi Quente De Armazenamento Em Nuvem",
|
||||
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Quente De Armazenamento Na Nuvem, Erro De Upload",
|
||||
"Watch": "Assistir",
|
||||
"Watch Only": "Assista Só",
|
||||
"Watch-Only": "Assistir-Apenas",
|
||||
"Watching": "Assistindo",
|
||||
"Web Page": "Página Da Web",
|
||||
"WebDAV": "WebDAV",
|
||||
"WebM (libvpx)": "WebM (libvpx)",
|
||||
"Webdav Error": "Webdav Erro",
|
||||
"WebdavErrorTextCreatingDir": "Não é possível criar diretório.",
|
||||
"WebdavErrorTextTryCreatingDir": "Não é possível salvar. Tentando criar o directório.",
|
||||
"Webhook": "Webhook",
|
||||
"Webhook URL": "Webhook URL",
|
||||
"Websocket": "Websocket",
|
||||
"Websocket Connected": "Websocket Conectado",
|
||||
"Websocket Disconnected": "Websocket Desconectado",
|
||||
"Width": "Largura",
|
||||
"X Point": "X Ponto",
|
||||
"Y Point": "Y Ponto De",
|
||||
"Yes": "Sim",
|
||||
"Zip and Download": "Zip e Download",
|
||||
"Zoom In": "Zoom No <small>Endereço de URL</small>",
|
||||
"Zoom In Stop": "Zoom Em Parar de <small>Endereço de URL</small>",
|
||||
"Zoom Out": "Zoom Out <small>Endereço de URL</small>",
|
||||
"Zoom Out Stop": "Zoom Out Parar <small>Endereço de URL</small>",
|
||||
"a day": "um dia",
|
||||
"a few seconds": "alguns segundos",
|
||||
"a minute": "um minuto",
|
||||
"a month": "um mês",
|
||||
"a year": "um ano",
|
||||
"aac": "aac",
|
||||
"aac (Default)": "aac (Padrão)",
|
||||
"ac3": "ac3",
|
||||
"accountId": "ID da conta",
|
||||
"ago": "atrás",
|
||||
"an hour": "uma hora",
|
||||
"applicationKey": "Chave De Aplicativo",
|
||||
"aws_accessKeyId": "Id Da Chave De Acesso",
|
||||
"aws_secretAccessKey": "A Chave De Acesso Secreta",
|
||||
"bindDN": "bindDN",
|
||||
"blankPassword": "Deixe em branco para manter a mesma palavra-passe",
|
||||
"clientStreamFailedattemptingReconnect": "Do lado do cliente ctream de verificação falhou, tentar reconectar.",
|
||||
"coProcess Crashed for Monitor": "coProcess Caiu para Monitor",
|
||||
"coProcess Unexpected Exit": "coProcess Inesperado Sair",
|
||||
"coProcessor": "co-processador",
|
||||
"coProcessor Started": "co-processador matemático Começou",
|
||||
"coProcessor Stopped": "co-processador Parado",
|
||||
"coProcessorTextStarted": "co-processador matemático foi iniciado pela CPU, apenas saídas.",
|
||||
"coProcessorTextStopped": "co-processador terminou.",
|
||||
"confirmDeleteFilter": "Você deseja excluir este filtro? Você não pode recuperá-lo.",
|
||||
"copy": "cópia",
|
||||
"cuvid": "cuvid (NVIDIA NVENC)",
|
||||
"days": "dias",
|
||||
"deleteMonitorStateText1": "Você deseja excluir este Monitor Estados Preset?",
|
||||
"deleteMonitorStateText2": "Você deseja excluir este Monitor do Preset?",
|
||||
"drm": "DRM objeto de partilha",
|
||||
"dropBoxSuccess": "Sucesso! Arquivos salvos em seu Dropbox.",
|
||||
"dxva2": "dxva2 (Vídeo DirectX, Windows)",
|
||||
"failedLoginText1": "Falhou o início de sessão muitas vezes. Você deve aguardar 15 minutos antes de tentar novamente.",
|
||||
"failedLoginText2": "Por favor verifique as suas credenciais de início de sessão.",
|
||||
"flv": "flv",
|
||||
"for Global Access": "para Acesso Global",
|
||||
"h264_cuvid": "H. 264 CUVID",
|
||||
"h264_mmal": "H. 264 (Raspberry Pi)",
|
||||
"h264_nvenc": "H. 264 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"h264_omx": "H. 264 openMAX (Raspberry Pi)",
|
||||
"h264_qsv": "H. 264 (Quick Sync Vídeo)",
|
||||
"h264_vaapi": "H. 264 VA-API (Intel HARDWARE de Aceleração)",
|
||||
"h265BrowserText1": "Se você estiver tentando reproduzir um H. 265 arquivo, você pode precisar para fazer o download e abri-lo em outro aplicativo, como o VLC.",
|
||||
"hevc_cuvid": "H. 265 CUVID",
|
||||
"hevc_nvenc": "H. 265 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"hevc_qsv": "H. 265 (Quick Sync Vídeo)",
|
||||
"hevc_vaapi": "H. 265 VA-API (Intel HARDWARE de Aceleração)",
|
||||
"hours": "horas",
|
||||
"hwaccel": "A Aceleração Do Motor",
|
||||
"hwaccel_device": "HWAccel Dispositivo",
|
||||
"hwaccel_vcodec": "O Decodificador De Vídeo",
|
||||
"in": "no",
|
||||
"in Days": "em Dias",
|
||||
"libmp3lame": "libmp3lame",
|
||||
"libopus": "libopus",
|
||||
"libvorbis (Default)": "libvorbis (Padrão)",
|
||||
"libvpx (Default)": "libvpx (Padrão)",
|
||||
"libvpx-vp9": "libvpx-vp9",
|
||||
"libx264": "libx264",
|
||||
"libx264 (Default)": "libx264 (Padrão)",
|
||||
"libx265": "libx265",
|
||||
"migrateText1": "<b>Tipo de entrada</b> não pode ser analisada. Por favor configure-a manualmente.",
|
||||
"minutes": "minutos",
|
||||
"mjpeg_cuvid": "MJPEG CUVID",
|
||||
"modifyVideoText1": "O método não existe. Certifique-se de que o último valor da URL não está em branco.",
|
||||
"monSavedButNotCopied": "O monitor foi salvo, mas não é copiado para qualquer outro monitor.",
|
||||
"monitorEditFailedMaxReached": "A tua conta atingiu o número máximo de câmeras que podem ser criados. Fale com um administrador se você gostaria de isso mudou.",
|
||||
"monitorEditText1": "Dados inválidos, Verifique para ver este é um pacote válido de importação de seqüência de caracteres.",
|
||||
"monitorEditText2": "Inválido Detalhes De Seqüência De Caracteres. Verifique para ver que é uma seqüência de caracteres JSON e não regular objeto que está sendo passado.",
|
||||
"monitorGetText1": "pedido incompleto, remova a última barra de URL ou colocar um valor aceitável.",
|
||||
"months": "meses",
|
||||
"mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
|
||||
"mpeg2_qsv": "MPEG2 (Quick Sync Vídeo)",
|
||||
"mpeg4_cuvid": "MPEG4 CUVID",
|
||||
"mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
|
||||
"noSpecialCharacters": "Sem espaços ou caracteres especiais.",
|
||||
"notPermitted1": "Esta ação não é permitido pelo administrador da sua conta.'",
|
||||
"on": "no",
|
||||
"on Error": "no Erro",
|
||||
"opencvCascadesText": "Se você não vê nada aqui, então, basta baixar este pacote de <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascatas</a>. Solte-os em <code>plugins/opencv/cascatas</code> , em seguida, pressione atualizar <i class=\"fa fa-retweet\"></i>.",
|
||||
"possibleInternalError": "Possível Erro Interno",
|
||||
"postDataBroken": "Verifique o formato JSON. Certifique-é stringified e definidos em 'dados'",
|
||||
"powerVideoEventLimit": "Você tem de definir um alto limite de evento. Tem certeza de que quer fazer esse pedido?",
|
||||
"privateKey": "Chave Privada",
|
||||
"qsv": "qsv",
|
||||
"sizePurgeLockedText": "O Tamanho de Purga de Bloqueio (deleteOverMax) parece ter falhado para desbloquear. Desbloqueio agora...",
|
||||
"skipPingText1": "Tente definir \"Ignorar Ping\" para Sim.",
|
||||
"startUpText0": "Verificação De Disco Usado..",
|
||||
"startUpText1": "Concluída A Verificação De Disco Utilizado.",
|
||||
"startUpText2": "todos os usuários marcada, aguarde para fechar arquivos abertos e remover arquivos de mais de limite de usuários",
|
||||
"startUpText3": "esperando para dar inacabado verificação de vídeo de algum tempo. 3 segundos.",
|
||||
"startUpText4": "Começando Monitores... Por Favor Aguarde...",
|
||||
"startUpText5": "Shinobi está pronto.",
|
||||
"startUpText6": "Órfão Vídeos Encontrados e Inserido",
|
||||
"superAdminText": "\"super.json\" não existe. Por favor, mude o nome \"super.exemplo.json\" para o \"super.json\".",
|
||||
"total": "total",
|
||||
"updateKeyText1": "\"updateKey\" está em falta \"conf.json\", não é possível fazer actualizações deste modo até que você o adicione.",
|
||||
"updateKeyText2": "\"updateKey\" está incorreta.",
|
||||
"vaapi": "vaapi (VA-API)",
|
||||
"vda": "vda (Apple VDA Aceleração de Hardware)",
|
||||
"vdpau": "vdpau",
|
||||
"videotoolbox": "videotoolbox",
|
||||
"vp8_cuvid": "VP8 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"vp8_qsv": "VP8 (Quick Sync Vídeo)",
|
||||
"vp9_cuvid": "VP9 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"years": "anos"
|
||||
}
|
909
languages/pt.old.json
Normal file
909
languages/pt.old.json
Normal file
|
@ -0,0 +1,909 @@
|
|||
{
|
||||
"\"No Motion\" Detector": "\"Sem movimento\" Detector",
|
||||
"# of Allow MJPEG Clients": "# para permitir clientes MJPEG <small>0 para infinito</small>",
|
||||
"180 Degrees": "180 Graus",
|
||||
"2-Factor Authentication": "Autenticação de 2 fatores",
|
||||
"90 Clockwise": "90 no sentido horário",
|
||||
"90 Clockwise and Vertical Flip": "90 no sentido horário e vertical",
|
||||
"90 Counter Clockwise and Vertical Flip (default)": "90 no sentido anti-horário e vertical (Padrão)",
|
||||
"AND": "E",
|
||||
"API": "API",
|
||||
"API Key": "Chave de API",
|
||||
"API Key Added": "Chave da API adicionada",
|
||||
"API Key Deleted": "Chave da API excluída",
|
||||
"API Keys": "Chaves da API",
|
||||
"APIKeyAddedText": "Você pode usar essa chave agora",
|
||||
"APIKeyDeletedText": "A chave foi excluída. Não funcionará mais.",
|
||||
"ASC": "ASC",
|
||||
"Accelerator": "Acelerador",
|
||||
"Account Info": "Informações da conta",
|
||||
"AccountEditText1": "Não foi possível editar. Atualize a página se o problema continuar",
|
||||
"Accounts": "Contas",
|
||||
"Action for Selected": "Ação para Selecionado",
|
||||
"Add": "Adicionar",
|
||||
"Add Channel": "Adicionar Canal",
|
||||
"Add Input Feed": "Adicionar Entrada De Alimentação",
|
||||
"Add Map": "Adicionar O Mapa",
|
||||
"Add Monitor": "Adicionar monitor",
|
||||
"Add New": "Adicionar novo",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avançado",
|
||||
"Again": "De novo",
|
||||
"Age": "Idade",
|
||||
"Alert Sound": "Som De Alerta",
|
||||
"Alert Sound Delay": "Som De Alerta Para Atraso",
|
||||
"All Logs": "Todos logs",
|
||||
"All Monitors": "Todos Monitores",
|
||||
"All Monitors and Privileges": "Todos monitores e privilégios",
|
||||
"All Privileges": "Todos Privilégios",
|
||||
"All Warnings": "Todos avisos",
|
||||
"All streams in first feed": "Todos os fluxos no primeiro feed",
|
||||
"Allow Next Command": "Habilitar próximo comando <small>em minutos</small>",
|
||||
"Allow Next Discord Alert": "Permitir Junto a Discórdia Alerta <small>em Minutos</small>",
|
||||
"Allow Next Email": "Permitir próximo e-mail <small>em minutos</small>",
|
||||
"Allow Next Trigger": "Habilitar próxima ação <small>em milissegundos</small>",
|
||||
"Allowed IPs": "IPs Permitidos",
|
||||
"Amazon S3": "O Amazon S3",
|
||||
"Amazon S3 Upload Error": "O Amazon S3 Erro De Upload",
|
||||
"Analyzation Duration": "Duração da análise",
|
||||
"Archive": "Arquivar",
|
||||
"Are you sure?": "Tem a certeza?",
|
||||
"Attach Video Clip": "Anexar Vídeo Clip",
|
||||
"Audio": "Áudio",
|
||||
"Audio Bit Rate": "Taxa De Bits De Áudio",
|
||||
"Audio Codec": "Codec De Áudio",
|
||||
"Audio Detection": "Detecção De Áudio",
|
||||
"Audio Detector": "Detector De Áudio",
|
||||
"Audio stream only from first feed": "Fluxo de áudio somente a partir de primeiro de alimentação",
|
||||
"Audio streams only": "Fluxos de áudio apenas",
|
||||
"Authenticate": "Autenticar",
|
||||
"Authentication Failed": "Autenticação falhou",
|
||||
"Auto": "Auto",
|
||||
"Automatic": "Automática",
|
||||
"Autosave": "Auto salvar",
|
||||
"Backblaze B2": "Backblaze B2",
|
||||
"Backblaze Error": "Backblaze Erro",
|
||||
"Base64 over Websocket": "Base64 sobre Websocket",
|
||||
"Basic Authentication": "Autenticação básica",
|
||||
"Batch": "Lote",
|
||||
"Bind Credentials": "Vincular Credenciais (Senha)",
|
||||
"Blank for No Change": "Em branco para Não Alterar",
|
||||
"Bottom Left": "Inferior esquerdo",
|
||||
"Bottom Right": "Inferior direito",
|
||||
"Browser Console Log": "Navegador de logs",
|
||||
"Bucket": "Balde",
|
||||
"Buffer Preview": "Buffer De Pré-Visualização",
|
||||
"CPU": "CPU",
|
||||
"CPU indicator will not work. Continuing...": "Indicador de CPU não irá funcionar. Continuando...",
|
||||
"CPU used by this stream": "CPU usada por este fluxo",
|
||||
"CSS": "CSS <small>Personalize seu painel de controle.</small>",
|
||||
"Calendar": "Calendário",
|
||||
"Call Method": "Método De Chamada",
|
||||
"Camera Password": "Senha da câmera",
|
||||
"Camera Username": "Usuário da câmera",
|
||||
"Camera is not recording": "Câmera não está gravando",
|
||||
"Camera is not running": "A câmera não está em execução",
|
||||
"Camera is not streaming": "Câmera não está transmitindo",
|
||||
"CameraNotRecordingText": "As configurações podem ser incompatíveis. Verifique os encoders. Reiniciando...",
|
||||
"Can Authenticate Websocket": "Pode Autenticar Websocket",
|
||||
"Can Change User Settings": "Pode Alterar As Definições De Utilizador",
|
||||
"Can Control Monitors": "Pode Controlar Monitores",
|
||||
"Can Create and Delete Monitors": "Pode Criar e Excluir Monitores",
|
||||
"Can Delete Videos": "Pode Deletar Vídeos",
|
||||
"Can Delete Videos and Events": "Pode Deletar Vídeos e Eventos",
|
||||
"Can Edit Monitor": "Pode Editar Monitores",
|
||||
"Can Get Logs": "Pode Obter Logs",
|
||||
"Can Get Monitors": "Pode Obter Monitores",
|
||||
"Can View Logs": "Pode Ver Os Logs",
|
||||
"Can View Monitor": "Pode Visualizar Monitores",
|
||||
"Can View Snapshots": "Pode Visualizar Snapshots",
|
||||
"Can View Streams": "Pode Visualizar Transmissões",
|
||||
"Can View Videos": "Pode Visualizar Vídeos",
|
||||
"Can View Videos and Events": "Pode Visualizar Vídeos e Eventos",
|
||||
"Can edit Max Days": "Pode editar Máximo de Dias",
|
||||
"Can edit Max Storage": "Pode editar Armazenamento Máximo",
|
||||
"Can edit how long to keep Events": "Pode editar quanto tempo manter eventos",
|
||||
"Can edit how long to keep Logs": "Pode editar por quanto tempo maneter logs",
|
||||
"Can use Admin Panel": "Pode usar Painel Administrativo",
|
||||
"Can use Amazon S3": "Pode usar o Amazon S3",
|
||||
"Can use Discord Bot": "Pode usar a Discórdia Bot",
|
||||
"Can use LDAP": "Pode usar LDAP",
|
||||
"Can use SFTP": "Pode usar SFTP",
|
||||
"Can use Wasabi Hot Cloud Storage": "Pode usar Wasabi Quente de Armazenamento em Nuvem",
|
||||
"Can use WebDAV": "Pode usar WebDAV",
|
||||
"Can't Connect": "Não pode conectar",
|
||||
"Cannot watch a monitor that isn't running.": "Não pode assistir um monitor que não está rodando.",
|
||||
"Cards": "Cartões",
|
||||
"Center": "Centro <small>URL Address</small>",
|
||||
"Channel": "Canal",
|
||||
"Channel ID": "ID do canal",
|
||||
"Chat on Discord": "Conversar no Discord",
|
||||
"Check": "Verificar",
|
||||
"Check Signal Interval": "Verifique o intervalo do sinal <small>em minutos</small>",
|
||||
"Check for Motion First": "Verifique primeiro o movimento",
|
||||
"Clear Recorder Process": "Claro Gravador De Processo",
|
||||
"Close": "Fechar",
|
||||
"Closed": "Fechado",
|
||||
"Color Threshold": "Limiar De Cores",
|
||||
"Command": "Comando",
|
||||
"Command on Trigger": "Comando quando acionado",
|
||||
"Complete Stream URL": "URL de transmissão completa",
|
||||
"Confidence": "Confiança",
|
||||
"Confidence of Detection": "A confiança de Detecção de",
|
||||
"Configuration": "Configuração",
|
||||
"Confirm": "Confirmar",
|
||||
"Connected": "Conectado",
|
||||
"Connection": "Conexão",
|
||||
"Connection Type": "Tipo de conexão",
|
||||
"Connection timed out": "Conexão esgotado",
|
||||
"Contains": "Contém",
|
||||
"Control": "Controle",
|
||||
"Control Error": "Erro de controle",
|
||||
"ControlErrorText1": "Controle não está ativado",
|
||||
"ControlErrorText2": "Verifique sua conexão detalhes. Você pode precisar ponto a URL Base em port 8000 ou 80. Verifique a sua informação de autenticação.",
|
||||
"Controllable": "Controlável",
|
||||
"Copy Connection Settings": "Copiar Configurações De Conexão",
|
||||
"Copy Custom Settings": "Copiar Configurações Personalizadas",
|
||||
"Copy Detector Settings": "Copiar As Configurações Do Detector",
|
||||
"Copy Group Settings": "Copiar Configurações Do Grupo",
|
||||
"Copy Input Settings": "Copiar As Configurações De Entrada De",
|
||||
"Copy JPEG API Settings": "Cópia JPEG API de Configurações",
|
||||
"Copy Logging Settings": "Copiar As Configurações De Log",
|
||||
"Copy Mode": "Modo De Cópia",
|
||||
"Copy Recording Settings": "Copiar Configurações De Gravação",
|
||||
"Copy Settings": "Configurações De Cópia",
|
||||
"Copy Stream Channel Settings": "Copiar Configurações De Canal Stream",
|
||||
"Copy Stream Settings": "Copiar Definições Da Sequência",
|
||||
"Copy to Settings": "Copiar Configurações",
|
||||
"Could not create Bucket.": "Não foi possível criar o Balde.",
|
||||
"Country of Plates": "País das placas",
|
||||
"Counts of Motion": "Contagem de movimento",
|
||||
"Create Sub-Accounts at /admin": "Criação de Sub-Contas em /admin",
|
||||
"Creating New Account": "Criando Nova Conta",
|
||||
"Creation Interval": "Intervalo De Criação De",
|
||||
"Current": "Atual",
|
||||
"Currently viewing": "Visualizando atualmente",
|
||||
"Custom": "Customizado",
|
||||
"Custom Base URL": "URL Customizada <small>Deixe em branco para usar URL do Host</small>",
|
||||
"Custom Endpoint": "Ponto Final Personalizado",
|
||||
"DB Lost.. Retrying..": "Banco de dados perdido... Repetindo...",
|
||||
"DESC": "DESC",
|
||||
"Dashboard": "Painel de Controle",
|
||||
"Dashboard Language": "Idioma do Painel de Controle",
|
||||
"Dashcam": "Dashcam",
|
||||
"Dashcam (Streamer v2)": "Dashcam (Streamer v2)",
|
||||
"Database": "Banco de dados",
|
||||
"Database Not Found": "Banco De Dados Não Encontrado",
|
||||
"Database row does not exist": "Linha de base de dados não existe",
|
||||
"Date Range": "Intervalo de Data",
|
||||
"Debug": "Debugar",
|
||||
"Default": "Padrão",
|
||||
"Delete": "Deletar",
|
||||
"Delete Filter": "Excluir filtro",
|
||||
"Delete Matches": "Deletar Combinações",
|
||||
"Delete Monitor": "Deletar Monitor",
|
||||
"Delete Monitor State?": "Apagar O Monitor De Estado",
|
||||
"Delete Monitor States Preset": "Apagar O Monitor De Estados Predefinidos",
|
||||
"Delete Motionless Video": "Deletar vídeos sem movimento",
|
||||
"Delete Motionless Videos (Record)": "Eliminar vídeos sem movimento (Gravação)",
|
||||
"Delete Selected Videos": "Excluir vídeos selecionados",
|
||||
"Delete Video": "Deletar Vídeo",
|
||||
"Delete selected": "Deletar Selecionado",
|
||||
"DeleteMonitorText": "Deseja excluir este monitor? Você não pode recuperá-lo. Os arquivos para essa ID permanecerão no sistema de arquivos. Se você optar por recriar um monitor com a mesma identificação, os vídeos e eventos tornar-se-ão visíveis no painel de controle.",
|
||||
"DeleteMonitorsText": "Você deseja excluir esses monitores? Você não pode recuperá-los. Você pode optar por manter os arquivos para essas Identificações no sistema de arquivos. Se você escolher para recriar um monitor com uma das Identificações vídeos e eventos tornam-se visíveis no painel de controle.",
|
||||
"DeleteSelectedVideosMsg": "Deseja excluir esses vídeos? Você não poderá recuperá-los.",
|
||||
"DeleteVideoMsg": "Deseja excluir este vídeo? Você não poderá recuperá-lo.",
|
||||
"Deleted": "Deletado",
|
||||
"Deleted Schedule Configuration": "Excluído Agenda De Configuração",
|
||||
"Deleted State Configuration": "Excluído O Estado De Configuração",
|
||||
"Detect Objects": "Detectar objetos <small class=\"\">Veja abaixo</small>",
|
||||
"Detection Engine": "O Motor De Detecção De",
|
||||
"Detector": "Detector de",
|
||||
"Detector Buffer": "Detector De Buffer",
|
||||
"Detector Filters": "Detector De Filtros",
|
||||
"Detector Flags": "Detector de Flags",
|
||||
"Detector Grouping": "Detector de Agrupamento <small>Adicionar grupos de <b>Configurações</b></small>",
|
||||
"Detector Rate": "Taxa do detector <small>(FPS)</small>",
|
||||
"Detector Recording Complete": "Detector De Gravação Completo",
|
||||
"Detector Recording Process Exited Prematurely. Restarting.": "Detector Processo De Gravação Foi Encerrado Prematuramente. Reiniciar.",
|
||||
"DetectorText": "<p>Quando as caixas de Largura e Altura são mostradas, você deve configurá-las para 640x480 ou abaixo. Isso otimizará a velocidade de leitura dos quadros.</p>",
|
||||
"Died": "Morreu",
|
||||
"Digest Authentication": "A Autenticação Digest",
|
||||
"Disable Night Vision": "Desativar visão noturna <small>URL Address</small>",
|
||||
"Disable Nightvision": "Desativar Visão Noturna",
|
||||
"Disabled": "Desabilitado",
|
||||
"Discord": "Discórdia",
|
||||
"Discord Alert on Trigger": "A discórdia Alerta no Gatilho",
|
||||
"Discord Bot": "A Discórdia Bot",
|
||||
"DiscordErrorText": "Enviar a Discórdia causou um Erro",
|
||||
"DiscordFailedText": "Enviar a Discórdia Falha",
|
||||
"DiscordLoggedIn": "A Discórdia Bot Autenticado",
|
||||
"DiscordNotEnabledText": "A discórdia Bot Não Habilitada, Habilite-o em suas Configurações de Conta.",
|
||||
"Documentation": "Documentação",
|
||||
"Does Not Contain": "Não Contém",
|
||||
"Don't show this anymore": "Não mostre isso mais",
|
||||
"Double Quote Directory": "Diretório com aspas duplas <small>Alguns diretórios têm espaços. Usar isso pode bloquear algumas câmeras.</small>",
|
||||
"Down": "Baixo <small>URL Address</small>",
|
||||
"Down Stop": "Baixo máximo <small>URL Address</small>",
|
||||
"Download": "Baixar",
|
||||
"EU": "UE",
|
||||
"Edit": "Editar",
|
||||
"Edit Selected": "Edit Selected",
|
||||
"Edited Schedule Configuration": "Editado Agenda De Configuração",
|
||||
"Edited State Configuration": "Editado Estado De Configuração",
|
||||
"Email": "E-mail",
|
||||
"Email Details": "Detalhes do E-mail",
|
||||
"Email address is in use.": "Endereço de e-mail está em uso.",
|
||||
"Email and Password fields cannot be empty": "E-mail e campos de palavra-Passe não pode ser vazio",
|
||||
"Email on No Motion": "E-mail em \"Sem movimento\"",
|
||||
"Email on Trigger": "E-mail quando acionado <small>Emails vão para o endereço de login do titular da conta principal.</small>",
|
||||
"Emotion": "Emoção",
|
||||
"Emotion Average": "Emoção Média",
|
||||
"Enable": "Habilitar",
|
||||
"Enable Night Vision": "Ativar visão noturna <small>URL Address</small>",
|
||||
"Enable Nightvision": "Ativar Visão Noturna",
|
||||
"Enabled": "Habilitado",
|
||||
"End": "Finalizar",
|
||||
"End Time": "Hora de Término",
|
||||
"Ended": "Finalizado",
|
||||
"Endpoint": "Ponto final",
|
||||
"Endpoint Address": "Endereço De Ponto De Extremidade",
|
||||
"Enlarge": "Prolongar",
|
||||
"Enter this code to proceed": "Informe este código para prosseguir",
|
||||
"Equal to": "Igual a",
|
||||
"Error Connecting": "Erro ao conectar",
|
||||
"Error While Decoding": "Erro Durante A Decodificação",
|
||||
"ErrorWhileDecodingText": "Seu hardware pode ter uma conexão instável para a rede. Verifique suas conexões de rede.",
|
||||
"Event": "Evento",
|
||||
"Event Limit": "Limite de Evento",
|
||||
"Event Webhook Error": "Evento Webhook Erro",
|
||||
"EventText1": "Disparou um evento de movimento",
|
||||
"EventText2": "Não foi possível enviar imagem de e-mail, o arquivo não foi acessado",
|
||||
"Events": "Eventos",
|
||||
"Example": "Exemplo",
|
||||
"Execute Command": "Executar Comando",
|
||||
"Executed": "Executado",
|
||||
"Export": "Exportar",
|
||||
"Export Selected Videos": "Exportar Vídeos Selecionados",
|
||||
"Export Video": "Exportação De Vídeo",
|
||||
"ExportSelectedVideosMsg": "Você deseja exportar esses vídeos? Pode levar algum tempo para zip e fazer o download.",
|
||||
"FFmpegCantStart": "FFmpeg não pôde iniciar",
|
||||
"FFmpegCantStartText": "O mecanismo de gravação para esta câmera não pôde começar. Pode haver algo errado com a configuração da sua câmera. Se houver algum registro diferente deste, por favor, coloque-os em <b> Problemas </b> no Github.",
|
||||
"FFmpegTip": "FFprobe é um simples fluxos multimídia analyzer. Você pode usá-lo para a saída de todos os tipos de informações sobre uma entrada, incluindo duração, taxa de quadros, tamanho do quadro, etc.",
|
||||
"FFprobe": "Sonda",
|
||||
"FLV": "FLV",
|
||||
"FLV Stream Type": "FLV Tipo de Fluxo",
|
||||
"FactorAuthText1": "O código vai estar ativo somente por 15 minutos. Se você entrar novamente o temporizador será reiniciado para 15 minutos com o mesmo código",
|
||||
"Fatal": "Fatal",
|
||||
"Fatal Maximum Reached": "Máximo atingido, parando câmera.",
|
||||
"FatalMaximumReachedText": "Erro JPEG fatal.",
|
||||
"Feed-in Image Height": "Altura da imagem de entrada",
|
||||
"Feed-in Image Width": "Largura da imagem de entrada",
|
||||
"Female": "Feminino",
|
||||
"Fields cannot be empty": "Campos não podem estar vazios",
|
||||
"File Delete Error": "Excluir O Arquivo De Erro",
|
||||
"File Not Exist": "Arquivo inexistente",
|
||||
"File Not Found": "Arquivo não encontrado",
|
||||
"File Not Found in Database": "Arquivo Não Encontrado no Banco de dados",
|
||||
"File Not Found in Filesystem": "Arquivo Não Encontrado no sistema de arquivos",
|
||||
"File Type": "Tipo de Arquivo",
|
||||
"FileNotExistText": "Não é possível salvar o arquivo inexistente. Algo deu errado.",
|
||||
"Filename": "Nome do Arquivo",
|
||||
"Filesize": "Tamanho do Arquivo",
|
||||
"Filter ID": "Filtrar ID",
|
||||
"Filter Matches": "Combinações de filtro",
|
||||
"Filter Name": "Filtrar Nome",
|
||||
"Filter for Objects only": "Filtro para que somente os Objetos",
|
||||
"FilterMatchesText1": "Este filtro atingiu as condições.",
|
||||
"FilterMatchesText2": "vídeos encontrados.",
|
||||
"Filters": "Filtros",
|
||||
"Filters Updated": "Filtros atualizados",
|
||||
"FiltersUpdatedText": "Suas alterações foram salvas e aplicadas.",
|
||||
"Find Where": "Encontrar Onde",
|
||||
"First stream in feed": "Primeiro fluxo de alimentação",
|
||||
"Fix": "Corrigir",
|
||||
"Fix Video": "Corrigir Vídeo",
|
||||
"FixVideoMsg": "Você deseja corrigir esse vídeo? Você não poderá desfazer essa ação..",
|
||||
"Flush PM2 Logs": "Flush Logs de PM2",
|
||||
"Font Path": "Caminho da fonte",
|
||||
"Font Size": "Tamanho da fonte",
|
||||
"For Group": "Para grupo",
|
||||
"Force Monitors Per Row": "Força De Monitores Por Linha",
|
||||
"Force Port": "Forçar porta",
|
||||
"Form Data Not Found": "Formulário De Dados Não Encontrado",
|
||||
"Found Devices": "Dispositivos encontrados",
|
||||
"Frame Rate": "Taxa de quadros <small>(FPS)</small>",
|
||||
"Full Frame Detection": "Detecção de quadro completo",
|
||||
"Full Stream URL": "Total URL de Fluxo",
|
||||
"Full URL Path": "Caminho completo da URL",
|
||||
"Fullscreen": "Tela cheia",
|
||||
"Gender": "Sexo",
|
||||
"Generate Subtitles": "Gerar legendas",
|
||||
"Get Logs to Client": "Obter Logs do Cliente",
|
||||
"Global Detector Settings": "Global As Configurações Do Detector",
|
||||
"Greater Than": "Maior que",
|
||||
"Greater Than or Equal to": "Maior ou Igual a",
|
||||
"Group Key": "Chave do Grupo",
|
||||
"Group Key is in use.": "Chave de grupo está em uso.",
|
||||
"Group Name": "Nome do grupo",
|
||||
"Grouping": "Agrupando <small>Adicione grupos em <b>Configurações</b></small>",
|
||||
"H.264 / H.265 / H.265+": "H. 264 / H. 265 / H. 265 ",
|
||||
"HEVC (H.265)": "HEVC (H. 265)",
|
||||
"HLS (.m3u8)": "HLS (.m3u8)",
|
||||
"HLS (includes Audio)": "HLS (inclui áudio)",
|
||||
"HLS Audio Encoder": "Codificador de áudio HLS",
|
||||
"HLS List Size": "Tamanho da lista HLS",
|
||||
"HLS Live Start Index": "HLS ao Vivo Índice de Início",
|
||||
"HLS Preset": "Pré-definição HLS",
|
||||
"HLS Segment Length": "Comprimento do segmento HLS <small>em segundos</small>",
|
||||
"HLS Start Number": "HLS Número de Início",
|
||||
"HLS Video Encoder": "Codificador de vídeo HLS",
|
||||
"HTTP": "HTTP",
|
||||
"HTTPS": "HTTPS",
|
||||
"Hardware Accelerated": "Aceleração De Hardware",
|
||||
"Height": "Altura",
|
||||
"Help": "Ajuda",
|
||||
"Hide List": "Esconder Lista",
|
||||
"Hide Notes": "Esconder Notas",
|
||||
"Host": "Host",
|
||||
"Host Type": "Tipo De Host",
|
||||
"Hotswap Modes (Watch-Only)": "Hotswap Modes (Visualizar Apenas)",
|
||||
"How to Record": "Como gravar",
|
||||
"IP Address": "Endereço IP",
|
||||
"Identity": "Identidade",
|
||||
"IdentityText1": "É assim que o sistema irá identificar os dados para este fluxo. Você não pode alterar o <b>ID do Monitor</b> uma vez que você pressionou salvar. Se você quiser, você pode fazer o <b>ID do Monitor</b> mais legível para humanos antes de continuar.",
|
||||
"IdentityText2": "Você pode duplicar um monitor modificando o <b>ID do Monitor</b> e depois pressionando salvar. Você <b>não pode</b> usar o ID de um monitor que já existe ou ele economizará sobre as informações do banco de dados desse monitor.",
|
||||
"Idle": "Inativo",
|
||||
"Image Height": "Altura da imagem",
|
||||
"Image Location": "Localização da imagem <small>Caminho Absoluto ou deixar em branco para usar global</small>",
|
||||
"Image Position": "Posição da imagem",
|
||||
"Image Width": "Largura da imagem",
|
||||
"Import": "Importar",
|
||||
"Import Monitor Configuration": "Importar Configuração do Monitor",
|
||||
"ImportMonitorConfigurationText": "Fazer isso substituirá todas as alterações atualmente não salvas. As mudanças importadas só serão aplicadas quando você pressionar <b>Salvar</b>.",
|
||||
"ImportMultiMonitorConfigurationText": "Fazendo isso vai overrwrite qualquer monitores com IDs existentes no arquivo de importação.",
|
||||
"In": "Em",
|
||||
"Incorrect Settings Chosen": "Configuração incorreta escolhida",
|
||||
"Indifference": "Indiferença",
|
||||
"Input": "Entrada",
|
||||
"Input Feed": "Entrada De Alimentação",
|
||||
"Input Flags": "Flags de entrada",
|
||||
"Input Selector": "Selector De Entrada",
|
||||
"Input Settings": "Configurações De Entrada De",
|
||||
"Input Type": "Tipo de entrada",
|
||||
"InputText1": "Esta seção diz a Shinobi como consumir um fluxo. Para um desempenho ideal, tente ajustar as configurações internas da sua câmera. Encontre as seguintes opções e defina-as como mostrado. Para encontrar sua câmera, você pode usar o <b>ONVIF Scanner embarcado</b> do Shinobi. Algumas câmeras ONVIF requerem o uso de uma ferramenta de gerenciamento para modificar suas configurações internas. Se você não consegue encontrar suas câmeras, você pode tentar <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">Gerenciador de dispositivos ONVIF para Windows</a>.",
|
||||
"InputText2": "<ul><li><b>Taxa de quadros por segundo (FPS) :</b> High : 10 - 15 FPS, Low : 2-5 FPS</li><li><b>I-frame interval :</b> 80</li><li><b>Bit Rate Type :</b> CBR (Constant Bit Rate)</li><li><b>Bit Rate :</b> entre 256kbps - 500kbps</li></ul>",
|
||||
"InputText3": "Se você precisar de ajuda para descobrir o tipo de entrada que sua câmera é, você pode dar uma olhada no <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Camera URLs List</a> na página do Shinobi.",
|
||||
"Inserted Schedule Configuration": "Inserido Agenda De Configuração",
|
||||
"Inserted State Configuration": "Inserido Estado De Configuração",
|
||||
"Invalid Data": "Dados Inválidos",
|
||||
"Invalid JSON": "JSON inválido",
|
||||
"InvalidJSONText": "Certifique-se de que esta seja uma string JSON válida para a configuração do monitor Shinobi.",
|
||||
"JPEG": "JPEG",
|
||||
"JPEG (Auto Enables JPEG API)": "JPEG (Auto habilitado JPEG API)",
|
||||
"JPEG API": "JPEG API <small>Snapshot (cgi-bin)</small>",
|
||||
"JPEG Error": "Erro JPEG",
|
||||
"JPEG Mode": "Modo JPEG",
|
||||
"JPEGErrorText": "Houve um problema ao obter dados da sua câmera.",
|
||||
"LDAP": "LDAP",
|
||||
"LDAP Success": "LDAP Sucesso",
|
||||
"LDAP User Authenticated": "Usuário LDAP Usuário Autenticado",
|
||||
"LDAP User is New": "Usuário LDAP é Novo",
|
||||
"Launch in New Window": "O lançamento na Nova Janela",
|
||||
"Leave blank for random.": "Deixe em branco para aleatório.",
|
||||
"Leave blank for unlimited": "Deixe em branco para ilimitado",
|
||||
"Left": "Esquerda <small>URL Address</small>",
|
||||
"Left Stop": "Esquerda máxima <small>URL Address</small>",
|
||||
"Less Than": "Menor que",
|
||||
"Less Than or Equal to": "Menor ou Igual a",
|
||||
"License Plate Detector": "Placa De Licença Detector De",
|
||||
"Like": "Como",
|
||||
"Limited": "Limitado",
|
||||
"Link Shinobi": "Link Shinobi",
|
||||
"Lisence Plate Detector": "Detector de placas",
|
||||
"List Toggle": "Mostrar lista",
|
||||
"List of Videos Delete Error": "Lista de Vídeos Apagar Erro",
|
||||
"Live Stream Toggle": "Mostrar transmissão ao vivo",
|
||||
"Live View": "Visualização ao vivo",
|
||||
"Local": "Local",
|
||||
"Log Level": "Nível de registro",
|
||||
"Log Signal Event": "Evento de sinal de registro <small>Apenas cliente</small>",
|
||||
"Log Stream": "Fluxo De Log",
|
||||
"Logging": "Registo",
|
||||
"Login": "Entrar",
|
||||
"Logout": "Sair",
|
||||
"Logs": "Logs",
|
||||
"Loop Stream": "Loop Stream",
|
||||
"MB": "MB",
|
||||
"MJPEG": "MJPEG",
|
||||
"MP4 (copy, libx264, libx265)": "MP4 (copiar, libx264, libx265)",
|
||||
"MPEG-4 (.mp4 / .ts)": "MPEG-4 (.mp4 / .ts)",
|
||||
"MPEG-DASH (includes Audio)": "MPEG-DASH (inclui Áudio)",
|
||||
"MailError": "MAIL ERROR : Não foi possível enviar e-mail, verifique conf.json. Ignorando qualquer recurso que dependa do envio",
|
||||
"Main": "Principal",
|
||||
"Male": "Masculino",
|
||||
"Manual": "Manual",
|
||||
"Map": "Mapa",
|
||||
"Matches": "Combina",
|
||||
"Max Indifference": "Max Indiferença",
|
||||
"Max Latency": "Max Latência",
|
||||
"Max Number of Cameras": "Número máximo de câmeras",
|
||||
"Max Storage Amount": "Montante máximo de armazenamento <small>em Megabytes</small>",
|
||||
"Maximum dB": "Máximo dB",
|
||||
"Merge Selected Videos": "Mesclar Vídeos Selecionados",
|
||||
"Merge Video": "Vídeo De Impressão",
|
||||
"Merge and Download": "De mala e fazer o Download",
|
||||
"MergeSelectedVideosMsg": "Você deseja mesclar esses vídeos? Pode levar algum tempo para mesclar e fazer o download. O momento em que a conexão é fechada, o arquivo será excluído. Certifique-se de manter o navegador aberto até que seja concluída.",
|
||||
"Migrator": "Migrator",
|
||||
"Minimum dB": "Mínimo dB",
|
||||
"Mode": "Modo",
|
||||
"Monitor": "Monitor",
|
||||
"Monitor Added by user": "Monitor adicionado pelo usuário",
|
||||
"Monitor Capture Rate": "Taxa de captura do monitor <small>(FPS)</small>",
|
||||
"Monitor Groups": "Grupos de monitores",
|
||||
"Monitor ID": "ID do Monitor",
|
||||
"Monitor Idling": "Monitor ocioso",
|
||||
"Monitor Name": "Nome do monitor",
|
||||
"Monitor Settings": "Configurações do monitor",
|
||||
"Monitor States": "Monitor De Estados",
|
||||
"Monitor States and Schedules": "Monitor de Estados e Horários",
|
||||
"Monitor Stopped": "Monitor parado",
|
||||
"Monitor Updated by user": "Monitor atualizado pelo usuário",
|
||||
"Monitor is now Disabled": "O Monitor é Desativado",
|
||||
"Monitor is now Idle": "O Monitor está agora Inactiva",
|
||||
"Monitor is now Recording": "O Monitor é agora de Gravação",
|
||||
"Monitor is now Watching": "O Monitor está agora Assistindo",
|
||||
"Monitor mode changed": "Modo de monitor mudou",
|
||||
"Monitor mode is already": "Modo de monitor já está em uso",
|
||||
"Monitor or Key does not exist.": "Monitor ou chave não existe",
|
||||
"MonitorIdlingText": "A sessão do monitor foi ordenada para ocioso.",
|
||||
"MonitorStoppedText": "A sessão do monitor foi ordenada a parar.",
|
||||
"Monitors": "Monitores",
|
||||
"Monitors per row": "Monitores por linha <small>para montagem</small>",
|
||||
"Monitors to Copy to": "Monitores para Copiar para",
|
||||
"Montage": "Montagem",
|
||||
"Motion": "Movimento",
|
||||
"Motion Detection": "Detecção De Movimento",
|
||||
"Motion GUI": "GUI de Movimento",
|
||||
"Motion Meter": "Medidos de Movimento",
|
||||
"Mp4Frag": "Mp4Frag",
|
||||
"Must be atleast one row": "Deve ser pelo menos uma linha",
|
||||
"Mute Audio": "Silenciar Áudio",
|
||||
"NVIDIA": "NVIDIA",
|
||||
"Name": "Nome",
|
||||
"Name cannot be empty.": "O nome não pode estar vazia.",
|
||||
"New Authentication Token": "Novo token de autenticação",
|
||||
"New Monitor": "Novo Monitor",
|
||||
"No": "Não",
|
||||
"No Audio": "Sem áudio",
|
||||
"No Data": "Sem Dados",
|
||||
"No Events found for this video": "Nenhum evento encontrado para este vídeo",
|
||||
"No Group with this key exists": "Nenhum grupo com esta chave foi encontrado",
|
||||
"No Monitor Exists with this ID.": "Não existem monitores com esse ID.",
|
||||
"No Monitor Found, Ignoring Request": "Monitor não encontrado, ignorando requisição",
|
||||
"No Monitor ID Present in Form": "Nenhum ID do Monitor Presente no Formulário",
|
||||
"No Monitors Selected": "Não Há Monitores Selecionados",
|
||||
"No Rotation": "Sem rotação",
|
||||
"No Videos Found": "Nenhum Vídeo Encontrado",
|
||||
"No such file": "Arquivo inexistente",
|
||||
"NoLogsFoundForDateRange": "Sem Registos encontrados neste intervalo de data. Tente alargar o intervalo de data.",
|
||||
"NoMotionEmailText1": "Nenhum movimento para",
|
||||
"NoMotionEmailText2": "Não houve nenhum movimento detectado na câmera para",
|
||||
"NoVideosFoundForDateRange": "Nenhum vídeo encontrado neste intervalo de datas. Tente definir a data de início mais adiante.",
|
||||
"Noise Filter": "Filtro De Ruído",
|
||||
"Noise Filter Range": "Filtro De Ruído Faixa De",
|
||||
"Not Authorized": "Não autorizados",
|
||||
"Not Connected": "Não conectado",
|
||||
"Not Equal to": "Não Igual a",
|
||||
"Not In": "Não Em",
|
||||
"Not Matches": "Não Combina",
|
||||
"Not Permitted": "Não permitidos",
|
||||
"Not Saved": "Não Salvo",
|
||||
"Not an Administrator Account": "Não é uma conta de Administrador",
|
||||
"NotAuthorizedText1": "Não autorizado, envie o comando inicial com \"auth\",\"ke\", e \"uid\"",
|
||||
"Notes": "Notas",
|
||||
"NotesPlacholder": "Comentários que você quer deixar para as configurações desta câmera.",
|
||||
"Nothing exists": "Nada existe",
|
||||
"Notification Sound": "Som De Notificação",
|
||||
"Notification Video Length": "Notificação De Duração Do Vídeo",
|
||||
"Number of Days to keep": "Número de dias para manter",
|
||||
"ONVIF": "ONVIF",
|
||||
"ONVIF Port": "ONVIF Porta",
|
||||
"ONVIF Scanner": "ONVIF Scanner",
|
||||
"ONVIFEventsNotAvailable": "ONVIF Eventos não Disponível",
|
||||
"ONVIFnotCompliantProfileT": "A câmera não está ONVIF Perfil T Complacente",
|
||||
"ONVIFnote": "Descubra os dispositivos ONVIF em redes fora da sua ou deixe em branco para escanear sua rede atual. <br>O nome de usuário e a senha podem ser deixados em branco.",
|
||||
"OR": "OU",
|
||||
"Object": "Objeto",
|
||||
"Object Detection": "A Detecção De Objetos",
|
||||
"Object Tag": "Objeto Tag",
|
||||
"OpenCV Cascades": "OpenCV Cascatas",
|
||||
"Operating Hours": "Horário De Funcionamento",
|
||||
"Options": "Opções",
|
||||
"Order Streams": "Ordenar transmissões",
|
||||
"Output Method": "Método de saída",
|
||||
"Password": "Senha",
|
||||
"Password Again": "Senha novamente",
|
||||
"Passwords don't match": "As senhas não combinam",
|
||||
"Paste JSON here.": "Cole o JSON aqui.",
|
||||
"Path": "Caminho",
|
||||
"Permissions": "Permissões",
|
||||
"Ping Failed": "O Ping",
|
||||
"Please Check Your Settings": "Por Favor Verifique As Suas Definições",
|
||||
"Please Wait for Completion": "Por favor, Aguarde a Conclusão, Dependendo do número de arquivos selecionados isso pode levar algum tempo. Atualizar para verificar novamente.",
|
||||
"Plugin": "Plugin",
|
||||
"Points": "Pontos <small>Ao adicionar pontos, clique na borda do polígono.</small>",
|
||||
"Pop": "Pop",
|
||||
"Popout Monitor on Event": "Popout Monitor de Eventos",
|
||||
"Port": "Porta",
|
||||
"Pose": "Pose",
|
||||
"Poseidon": "Poseidon",
|
||||
"Position X": "Posição X",
|
||||
"Position Y": "Posição Y",
|
||||
"Power Video Viewer": "Ligar visualizador de vídeo",
|
||||
"Power Viewer": "Ligar visualizador",
|
||||
"Preferences": "Preferências",
|
||||
"Preset": "Pré-definição",
|
||||
"Presets": "Predefinições",
|
||||
"Preview": "Visualização",
|
||||
"Primary Engine": "Motor Principal",
|
||||
"Primary Input": "Entrada Principal",
|
||||
"Privileges": "Privilégios",
|
||||
"Probe Size": "Sonda Tamanho",
|
||||
"Process Crashed for Monitor": "Processo de Monitor quebrado",
|
||||
"Process Started": "Processo Iniciado",
|
||||
"Process Unexpected Exit": "Saída inesperado do processo",
|
||||
"Processor": "Processador",
|
||||
"Profile": "Perfil",
|
||||
"Quality": "Qualidade <small>1 para alta, 23 para Low</small>",
|
||||
"Query": "Pesquisa",
|
||||
"Quick Sync Video": "Quick Sync Video",
|
||||
"RAM": "RAM",
|
||||
"RTMP": "RTMP",
|
||||
"RTMP Stream": "RTMP Stream",
|
||||
"RTMP Stream Flags": "RTMP Fluxo de Sinalizadores",
|
||||
"RTMPS": "RTMPS",
|
||||
"RTSP": "RTSP",
|
||||
"RTSP Transport": "Transporte RTSP",
|
||||
"Range or Single": "Intervalo ou Único",
|
||||
"Raspberry Pi": "Raspberry Pi",
|
||||
"Rate": "Taxa <small>(FPS)</small>",
|
||||
"Raw": "Matérias",
|
||||
"Raw H.264 Stream": "Matérias H. 264 Stream",
|
||||
"Reason": "Razão",
|
||||
"Recent Videos": "Vídeos Recentes",
|
||||
"Recommended": "Recomendado",
|
||||
"Record": "Gravar",
|
||||
"Record File Type": "Tipo de arquivo de gravação",
|
||||
"Record Height": "Altura da gravação",
|
||||
"Record Video Filter": "Gravar filtro de vídeo",
|
||||
"Record Width": "Largura da gravação",
|
||||
"Recorded Buffer": "Gravado Buffer",
|
||||
"Recording": "Gravando",
|
||||
"Recording FPS": "Gravação de FPS",
|
||||
"Recording FPS Change on Start": "Gravação de FPS Alterar em Iniciar",
|
||||
"Recording Flags": "Gravando Flags",
|
||||
"Recording Segment Interval": "Intervalo de Segmento de Gravação <small>em minutos</small>",
|
||||
"Recording Timeout": "Gravando Timeout <small>em minutos</small>",
|
||||
"Recording Timestamp": "Gravando horário",
|
||||
"Recording Watermark": "Gravando marca d'água",
|
||||
"RecordingText": "Recomenda-se que você defina <b>Tipo de arquivo de gravação</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">MP4</b> e <b>Video Codec</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copie ou </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> porque seu <b>Tipo de Entrada</b> está definido para <b class=\"h_t_text\"></b>.",
|
||||
"Refresh List of Cascades": "Atualizar Lista de Cascatas",
|
||||
"Region": "Região",
|
||||
"Region Editor": "Editor de região",
|
||||
"Region Name": "Nome da região",
|
||||
"RegionNote": "Os pontos só são salvos quando você pressiona <b>Salvar</b> na janela de <b>Configurações do Monitor</b>.",
|
||||
"Regions": "Regiões",
|
||||
"Remember Me": "Lembrar",
|
||||
"Request": "Pedido",
|
||||
"Require Object to be in Region": "Exigir Objeto a ser na Região",
|
||||
"Reset Timer": "Temporizador de reinicialização",
|
||||
"Restart": "Reinicie",
|
||||
"Restart CRON": "Reinicie o CRON",
|
||||
"Restart Core": "Reinicie O Núcleo",
|
||||
"Restarting": "Reiniciar",
|
||||
"Restarting Process": "Reiniciado processo",
|
||||
"Retry Connection": "Tentar novamente a conexão <small>Número de vezes permitido falhar</small>",
|
||||
"Retrying...": "Repetindo...",
|
||||
"Right": "Direita <small>URL Address</small>",
|
||||
"Right Stop": "Direita máxima <small>URL Address</small>",
|
||||
"Rotate": "Rotação",
|
||||
"S3-Based Network Storage": "S3-Armazenamento Em Rede",
|
||||
"SFTP": "SFTP",
|
||||
"SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)",
|
||||
"SFTP Error": "SFTP Erro",
|
||||
"Save": "Salvar",
|
||||
"Save Directory": "Salvar diretório",
|
||||
"Save Events to SQL": "Salvar eventos no SQL",
|
||||
"Save Frames to Events": "Salvar Quadros para Eventos",
|
||||
"Save Links to Database": "Salvar Links para Banco de dados",
|
||||
"Save Log in SQL": "Salvar log em SQL <small>Isso pode encher rapidamente.</small>",
|
||||
"Save as": "Salvar como",
|
||||
"Saved Filters": "Filtros Salvos",
|
||||
"Saved Presets": "Salvo Presets",
|
||||
"Saved Schedules": "Salvo Horários",
|
||||
"Scan Settings": "Configurações de digitalização",
|
||||
"Schedule": "Programação",
|
||||
"Schedule Configuration Not Found": "Agenda De Configuração Não Encontrado",
|
||||
"Schedules": "Horários",
|
||||
"Search": "Procurar",
|
||||
"Search Base": "Base de Pesquisa",
|
||||
"Search Filter": "Procurar Filtro",
|
||||
"Search Images": "Procurar Imagens",
|
||||
"Second stream in feed": "Segundo fluxo de alimentação",
|
||||
"Secure": "Seguro",
|
||||
"Send Frames": "Enviar quadros <small>Envie os quadros a serem analisados</small>",
|
||||
"Separate with commas, no spaces": "Separar com vírgulas, sem espaços",
|
||||
"Server URL": "URL do servidor",
|
||||
"Set to Watch Only": "Definir para apenas visualizar",
|
||||
"Settings": "Configurações",
|
||||
"Settings Changed": "Configurações Alteradas",
|
||||
"SettingsChangedText": "Suas configurações foram salvas e aplicadas.",
|
||||
"Shinobi": "Shinobi",
|
||||
"Shinobi Streamer": "Shinobi Streamer",
|
||||
"Show Logs": "Mostrar logs",
|
||||
"Show Matrices": "Mostrar Matrizes",
|
||||
"Show Matrix": "Mostrar A Matriz",
|
||||
"Show Regions of Interest": "Mostrar as Regiões de Interesse",
|
||||
"Show Stream HUD": "Show Stream HUD",
|
||||
"Show Thumbnails in Video List": "Mostrar Miniaturas na Lista de filmes",
|
||||
"Silent": "Silencioso",
|
||||
"Simple": "Simples",
|
||||
"Size (mb)": "Tamanho (mb)",
|
||||
"Skip Ping": "Pular Ping",
|
||||
"Snapshot": "Instantâneo",
|
||||
"Snapshot Flags": "Instantâneo Bandeiras",
|
||||
"Snapshots": "Instantâneos",
|
||||
"Sort By": "Ordenar Por",
|
||||
"Space Used": "Espaço Utilizado",
|
||||
"Start": "Iniciar",
|
||||
"Start Recording": "Iniciar gravação",
|
||||
"Start Time": "Hora de Início",
|
||||
"Start Time cannot be empty.": "Horário de início não pode estar vazia.",
|
||||
"Started": "Iniciado",
|
||||
"Starting": "Partida",
|
||||
"State Configuration Not Found": "Estado De Configuração Não Encontrado",
|
||||
"State Configuration has no monitors associated": "Configuração de estado não tem monitores associados",
|
||||
"Status Changed": "Status Alterado",
|
||||
"Status Indicator": "Indicador de status",
|
||||
"Stop": "Parar",
|
||||
"Stop Command": "Comando De Parada",
|
||||
"Stop URL": "Parar URL",
|
||||
"Stopped": "Parado",
|
||||
"Storage Location": "Local De Armazenamento",
|
||||
"Stream": "Transmissão",
|
||||
"Stream Channel": "Canal De Transmissão Em Sequência",
|
||||
"Stream Flags": "Flags de Transmissão",
|
||||
"Stream Key": "Chave De Fluxo",
|
||||
"Stream Timestamp": "Transmitindo horário",
|
||||
"Stream Type": "Tipo de transmissão",
|
||||
"Stream Watermark": "Transmitindo marca d'água",
|
||||
"Stream to YouTube": "Transmitir para o YouTube",
|
||||
"Stream to YouTube Flags": "Transmitir para o YouTube Flags",
|
||||
"StreamText": "<p>Esta seção designará o método do fluxo primário e as configurações. Este fluxo será exibido no painel. Se você optar por usar HLS, JPEG ou MJPEG, então você pode consumir o fluxo através de outros programas.</p><p class=\"h_st_input h_st_jpeg\">Usar o fluxo de JPEG essencialmente desativa o fluxo primário e usa o compartimento de instantâneos para obter quadros.</p>",
|
||||
"Streamer": "Transmissor",
|
||||
"Streams": "Transmissões",
|
||||
"Subdivision": "Subdivisão",
|
||||
"Success": "Sucesso",
|
||||
"Superuser": "Superusuário",
|
||||
"Superuser Logs": "Logs do Superusuário",
|
||||
"Switch on for Still Image": "Ligar para manter imagem",
|
||||
"System": "Sistema",
|
||||
"TCP": "TCP",
|
||||
"TV Channel": "TV Canal",
|
||||
"TV Channel Group": "Canal de TV do Grupo",
|
||||
"TV Channel ID": "TV Canal ID",
|
||||
"Text Box Color": "Cor da caixa de texto",
|
||||
"Text Color": "Cor do texto",
|
||||
"Themes": "Temas",
|
||||
"There are no monitors that you can view with this account.": "Não há monitores que você pode ver com esta conta.",
|
||||
"Thumbnail": "Miniatura",
|
||||
"Time": "Horário",
|
||||
"Time Left": "Tempo Restante",
|
||||
"Time Occurred": "Tempo Ocorreu",
|
||||
"Time-lapse": "Time-lapse",
|
||||
"Time-lapse Tool": "Ferramenta Time-laps",
|
||||
"Timelapse": "Timelapse",
|
||||
"Timeout": "Tempo de espera",
|
||||
"Timeout Reset on Next Event": "Tempo limite de Reset no Próximo Evento",
|
||||
"Timeout Reset on Next Motion": "Timeout reiniciará no próximo movimento",
|
||||
"Toggle Sidebar": "Mostrar barra lateral",
|
||||
"Token": "Token",
|
||||
"Top Left": "Superior esquerdo",
|
||||
"Top Right": "Superior direito",
|
||||
"Traditional (Watch-Only, Includes Buffer)": "Tradicional (Veja Só, Inclui Buffer)",
|
||||
"Traditional Recording": "Tradicional De Gravação",
|
||||
"Traditional Recording Flags": "A Tradicional Gravação De Sinalizadores",
|
||||
"Train": "De trem",
|
||||
"TrainConfirm": "Tem certeza de que deseja para começar a treinar? Isto pode demorar mais de 12 horas, com mais de 500 imagens. Isto irá consumir uma grande quantidade de recursos, como memória RAM ou CPU.",
|
||||
"Trainer Engine": "Treinador Do Motor",
|
||||
"Trigger Camera Groups": "Disparador Da Câmera Grupos",
|
||||
"Trigger Group to Record": "Gatilho do Grupo para Gravar",
|
||||
"Trigger Record": "Acionar registro",
|
||||
"Trigger Successful": "Disparou bem sucedido",
|
||||
"Trigger Threshold": "Gatilho Limite",
|
||||
"UDP": "UDP",
|
||||
"URL": "URL",
|
||||
"URL Stop Timeout": "URL timeout máximo <small>Parar URL depois de X milissegundos</small>",
|
||||
"US": "EUA",
|
||||
"Unable to Launch": "Incapaz de executar",
|
||||
"UnabletoLaunchText": "Salve primeiro o monitor novo. Em seguida, tente iniciar o editor da região.",
|
||||
"Uniform": "Uniforme",
|
||||
"Up": "Cima <small>URL Address</small>",
|
||||
"Up Stop": "Cima máximo <small>URL Address</small>",
|
||||
"Update": "Atualização",
|
||||
"Update to Development": "Actualização para o Desenvolvimento",
|
||||
"Update to Master": "Atualização para o Mestre de",
|
||||
"Upload File": "Upload Do Arquivo",
|
||||
"Uploaders": "Uploaders",
|
||||
"Use Built-In": "Use Built-In",
|
||||
"Use Global Amazon S3 Video Storage": "Uso Global Do Amazon S3 Armazenamento De Vídeo",
|
||||
"Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Armazenamento De Vídeo",
|
||||
"Use Global Wasabi Hot Cloud Storage Video Storage": "Uso Global Do Wasabi Quente De Armazenamento Em Nuvem De Armazenamento De Vídeo",
|
||||
"Use Global WebDAV Video Storage": "Uso Global Do WebDAV Armazenamento De Vídeo",
|
||||
"Use HTML5 Play Method": "Utilizar o HTML5 Método Play",
|
||||
"Use Max Storage Amount": "Usar O Max Quantidade De Armazenamento",
|
||||
"Use coProcessor": "O uso de co-processador",
|
||||
"User Not Found": "Usuário Não Encontrado",
|
||||
"Username": "Usuário",
|
||||
"VA-API": "VA-API",
|
||||
"Value": "Valor",
|
||||
"Video": "Vídeo",
|
||||
"Video Bit Rate": "Taxa De Bits De Vídeo",
|
||||
"Video Codec": "Codec De Vídeo",
|
||||
"Video Filter": "Filtro de vídeo",
|
||||
"Video Finished": "Vídeo finalizado",
|
||||
"Video Length (minutes) and Motion Count per video": "Comprimento do vídeo (minutos) e Contagem de movimento por vídeo",
|
||||
"Video Limit": "Limite de vídeo",
|
||||
"Video Record Rate": "Taxa de gravação de vídeo <small>(FPS)</small>",
|
||||
"Video Set": "Conjunto De Vídeo",
|
||||
"Video Status": "Status do Vídeo",
|
||||
"Video and Time Span (Minutes)": "Vídeo e intervalo de tempo (minutos)",
|
||||
"Video stream only from first feed": "Fluxo de vídeo somente a partir de primeiro de alimentação",
|
||||
"Video streams only": "Fluxos de vídeo informativo",
|
||||
"Videos": "Vídeos",
|
||||
"Videos List": "Lista de vídeos",
|
||||
"Videos Merge": "Vídeos De Mesclagem",
|
||||
"Warning": "Aviso",
|
||||
"Wasabi Hot Cloud Storage": "Wasabi Quente De Armazenamento Em Nuvem",
|
||||
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Quente De Armazenamento Na Nuvem, Erro De Upload",
|
||||
"Watch": "Assistir",
|
||||
"Watch Only": "Visualizar apenas",
|
||||
"Watch-Only": "Assistir-Apenas",
|
||||
"Watching": "Assistindo",
|
||||
"Web Page": "Página Da Web",
|
||||
"WebDAV": "WebDAV",
|
||||
"WebM (libvpx)": "WebM (libvpx)",
|
||||
"Webdav Error": "Erro Webdav",
|
||||
"WebdavErrorText": "Não é possível salvar. Você criou os diretórios da câmera dentro do seu diretório de escolhido para isso?",
|
||||
"WebdavErrorTextCreatingDir": "Não é possível criar diretório.",
|
||||
"WebdavErrorTextTryCreatingDir": "Não é possível salvar. Tentando criar o directório.",
|
||||
"Webhook": "Webhook",
|
||||
"Webhook URL": "Webhook URL",
|
||||
"Websocket": "Websocket",
|
||||
"Websocket Connected": "Websocket conectado",
|
||||
"Websocket Disconnected": "Websocket desconectado",
|
||||
"Width": "Largura",
|
||||
"X Point": "X Ponto",
|
||||
"Y Point": "Y Ponto De",
|
||||
"Yes": "Sim",
|
||||
"Zip and Download": "Zip e Download",
|
||||
"Zoom In": "Mais Zoom <small>URL Address</small>",
|
||||
"Zoom In Stop": "Parar mais zoom <small>URL Address</small>",
|
||||
"Zoom Out": "Menos Zoom <small>URL Address</small>",
|
||||
"Zoom Out Stop": "Parar menos zoom <small>URL Address</small>",
|
||||
"a day": "um dia",
|
||||
"a few seconds": "alguns segundos",
|
||||
"a minute": "um minuto",
|
||||
"a month": "um mês",
|
||||
"a year": "um ano",
|
||||
"aac": "aac",
|
||||
"aac (Default)": "aac (Padrão)",
|
||||
"ac3": "ac3",
|
||||
"accountId": "ID da conta",
|
||||
"ago": "atrás",
|
||||
"an hour": "uma hora",
|
||||
"applicationKey": "Chave De Aplicativo",
|
||||
"aws_accessKeyId": "Id Da Chave De Acesso",
|
||||
"aws_secretAccessKey": "A Chave De Acesso Secreta",
|
||||
"bindDN": "Vincular DN",
|
||||
"blankPassword": "Deixe em branco para manter a mesma senha",
|
||||
"clientStreamFailedattemptingReconnect": "A verificação ctream do lado do cliente falhou, tentando reconectar.",
|
||||
"coProcess Crashed for Monitor": "coProcess Caiu para Monitor",
|
||||
"coProcess Unexpected Exit": "coProcess Inesperado Sair",
|
||||
"coProcessor": "co-processador",
|
||||
"coProcessor Started": "co-processador matemático Começou",
|
||||
"coProcessor Stopped": "co-processador Parado",
|
||||
"coProcessorTextStarted": "co-processador matemático foi iniciado pela CPU, apenas saídas.",
|
||||
"coProcessorTextStopped": "co-processador terminou.",
|
||||
"confirmDeleteFilter": "Deseja excluir este filtro? Você não poderá recuperá-lo.",
|
||||
"copy": "copiar",
|
||||
"cuvid": "cuvid (NVIDIA NVENC)",
|
||||
"days": "dias",
|
||||
"deleteMonitorStateText1": "Você deseja excluir este Monitor Estados Preset?",
|
||||
"deleteMonitorStateText2": "Você deseja excluir este Monitor do Preset?",
|
||||
"drm": "DRM objeto de partilha",
|
||||
"dropBoxSuccess": "Sucesso! Arquivos salvos em seu Dropbox.",
|
||||
"dxva2": "dxva2 (Vídeo DirectX, Windows)",
|
||||
"failedLoginText1": "Falhou o início de sessão muitas vezes. Você deve aguardar 15 minutos antes de tentar novamente.",
|
||||
"failedLoginText2": "Por favor verifique as suas credenciais de início de sessão.",
|
||||
"flv": "flv",
|
||||
"for Global Access": "para Acesso Global",
|
||||
"h264_cuvid": "H. 264 CUVID",
|
||||
"h264_mmal": "H. 264 (Raspberry Pi)",
|
||||
"h264_nvenc": "H. 264 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"h264_omx": "H. 264 openMAX (Raspberry Pi)",
|
||||
"h264_qsv": "H. 264 (Quick Sync Vídeo)",
|
||||
"h264_vaapi": "H. 264 VA-API (Intel HARDWARE de Aceleração)",
|
||||
"h265BrowserText1": "Se você estiver tentando reproduzir um H. 265 arquivo, você pode precisar para fazer o download e abri-lo em outro aplicativo, como o VLC.",
|
||||
"hevc_cuvid": "H. 265 CUVID",
|
||||
"hevc_nvenc": "H. 265 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"hevc_qsv": "H. 265 (Quick Sync Vídeo)",
|
||||
"hevc_vaapi": "H. 265 VA-API (Intel HARDWARE de Aceleração)",
|
||||
"hours": "horas",
|
||||
"hwaccel": "A Aceleração Do Motor",
|
||||
"hwaccel_device": "HWAccel Dispositivo",
|
||||
"hwaccel_vcodec": "O Decodificador De Vídeo",
|
||||
"ifYouSeeNothingHereThenJustDownloadThisPackageOfCascadesDropThemIntoPluginsopencvcascadesThenPressRefresh": "Se você não vê nada aqui, então, basta baixar este pacote de <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascatas</a>. Solte-os em <code>plugins/opencv/cascatas</code> , em seguida, pressione atualizar <i class=\"fa fa-retweet\"></i>.",
|
||||
"in": "em",
|
||||
"in Days": "em Dias",
|
||||
"itIsRecommendedThatYouSetRecordFileTypeToWebmmp4AndVideoCodecToLibvpxcopyOrLibx264BecauseYourInputTypeIsSetTo": "É recomendável que você defina o <b>Registro de Tipo de Arquivo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">, MP4</b> e <b>Codec de Vídeo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copiar ou </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> porque o <b>Tipo de Entrada</b> é definida como <b class=\"h_t_text\"></b>.",
|
||||
"libmp3lame": "libmp3lame",
|
||||
"libopus": "libopus",
|
||||
"libvorbis (Default)": "libvorbis (Padrão)",
|
||||
"libvpx (Default)": "libvpx (Padrão)",
|
||||
"libvpx-vp9": "libvpx-vp9",
|
||||
"libx264": "libx264",
|
||||
"libx264 (Default)": "libx264 (Padrão)",
|
||||
"libx265": "libx265",
|
||||
"migrateText1": "<b>Tipo de entrada</b> não pode ser analisada. Por favor configure-a manualmente.",
|
||||
"minutes": "minutos",
|
||||
"mjpeg_cuvid": "MJPEG CUVID",
|
||||
"modifyVideoText1": "Método não existe. Verifique se o último valor da URL não está em branco",
|
||||
"monSavedButNotCopied": "O monitor foi salvo, mas não é copiado para qualquer outro monitor.",
|
||||
"monitorEditFailedMaxReached": "Sua conta atingiu o número máximo de câmeras que podem ser criadas. Fale com um administrador se você gostaria que isso mudasse.",
|
||||
"monitorEditText1": "Dados inválidos. Verifique se esta é uma string de importação válida.",
|
||||
"monitorEditText2": "String de detalhes inválida. Certifique-se de ser uma string JSON e não um objeto comum que está sendo informado",
|
||||
"monitorGetText1": "Requisição incompleta. Remova a última barra da URL ou informe um valor válido",
|
||||
"months": "meses",
|
||||
"mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
|
||||
"mpeg2_qsv": "MPEG2 (Quick Sync Vídeo)",
|
||||
"mpeg4_cuvid": "MPEG4 CUVID",
|
||||
"mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
|
||||
"noSpecialCharacters": "Sem espaços ou caracteres especiais.",
|
||||
"notPermitted1": "Esta ação não é permitido pelo administrador da sua conta.'",
|
||||
"on": "ligado",
|
||||
"on Error": "em Erro",
|
||||
"opencvCascadesText": "Se você não vê nada aqui, então, basta baixar este pacote de <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascatas</a>. Solte-os em <code>plugins/opencv/cascatas</code> , em seguida, pressione atualizar <i class=\"fa fa-retweet\"></i>.",
|
||||
"possibleInternalError": "Possível Erro Interno",
|
||||
"postDataBroken": "Verifique o formato JSON. Certifique-é stringified e definidos em 'dados'",
|
||||
"powerVideoEventLimit": "Você tem de definir um alto limite de evento. Tem certeza de que quer fazer esse pedido?",
|
||||
"privateKey": "Chave Privada",
|
||||
"qsv": "qsv",
|
||||
"sizePurgeLockedText": "O Tamanho de Purga de Bloqueio (deleteOverMax) parece ter falhado para desbloquear. Desbloqueio agora...",
|
||||
"skipPingText1": "Tente definir \"Ignorar Ping\" para Sim.",
|
||||
"startUpText0": "Verificação de tamanho para vídeos",
|
||||
"startUpText1": "Verificação de fim de tamanho para vídeos",
|
||||
"startUpText2": "Todos usuário verificados. Aguarde para fechar arquivos abertos e remover arquivos por limite de usuário",
|
||||
"startUpText3": " Esperando dar uma verificação de vídeo inacabada algum tempo. 3 segundos",
|
||||
"startUpText4": "Iniciando todos os monitores para assistir e gravar",
|
||||
"startUpText5": "Shinobi está pronto.",
|
||||
"startUpText6": "Órfão Vídeos Encontrados e Inserido",
|
||||
"superAdminText": "\"super.json\" não existe. Por favor, renomeie \"super.sample.json\" para \"super.json\".",
|
||||
"superAdminTitle": "Shinobi : Super Admin",
|
||||
"thisIsHowTheSystemWillIdentifyTheDataForThisStreamYouCannotChangeTheMonitorIdOnceYouHavePressedSaveIfYouWantYouCanMakeTheMonitorIdMoreHumanReadableBeforeYouContinueyouCanDuplicateAMonitorByModifyingTheMonitorIdThenPressingSaveYouCannotUseTheIdOfAMonitorThatAlreadyExistsOrItWillSaveOverThatMonitorsDatabaseInformation": "<div class=\"am_notice am_notice_new\">Esta é a forma como o sistema irá identificar os dados para este fluxo. Você não pode alterar o <b>ID do Monitor</b> depois de ter pressionado salvar. Se você quiser você pode fazer o <b>Monitor de IDENTIFICAÇÃO</b> mais legível para humanos antes de continuar.</div><div class=\"am_notice am_notice_edit\">Você pode duplicar um monitor modificando o <b>Monitor de IDENTIFICAÇÃO</b> , em seguida, pressionando salvar. Você <b>não pode</b> usar o ID de um monitor que já existe, ou ele vai economizar mais que o monitor de informações do banco de dados.</div>",
|
||||
"thisSectionTellsShinobiHowToConsumeAStreamForOptimalPerformanceTryTuningYourCamerasInternalSettingsFindTheFollowingOptionsAndSetThemAsShownToFindYourCameraYouCanUseTheBuiltInOnvifScannerOfShinobiSomeOnvifCamerasRequireTheUseOfAManagementToolToModifyTheirInternalSettingsIfYouCantFindYourCamerasYouCanTryOnvifDeviceManagerForWindowsFramerateFpsHigh1015FpsLow25FpsiframeInterval1bitRateTypeVbrVariableBitRatebitRateBetween256kbps1000kbpsIfYouNeedHelpFiguringOutWhatInputTypeYourCameraIsYouCanTakeALookInTheCameraUrlsListOnTheShinobiWebsite": "<p>Esta secção diz Shinobi como consumir um fluxo. Para melhor desempenho, tente afinar a sua câmera configurações internas. Encontrar as seguintes opções e defina-as como mostrado. Para encontrar a sua câmera, você pode usar o <b>construída em ONVIF Scanner</b> de Shinobi. Alguns ONVIF câmeras exigem o uso de uma ferramenta de gestão para modificar suas configurações internas. Se você não pode encontrar câmeras, você pode tentar <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">ONVIF Gerenciador de Dispositivos do Windows</a>.</p> <ul><li><b>A taxa de quadros (FPS) :</b> Alta : de 10 a 15 FPS, Baixa : de 2 a 5 FPS</li><li><b>I-intervalo :</b> 1</li><li><b>A Taxa de bits do Tipo :</b> VBR (Taxa de Bit Variável)</li><li><b>Taxa de bits :</b> entre 256kbps - 1000kbps</li></ul> <p>Se você precisar de ajuda para descobrir que tipo de entrada de sua câmera é que você pode dar uma olhada na <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Câmera Lista de URLs</a> no Shinobi site.</p>",
|
||||
"thisSectionWillDesignateThePrimaryMethodOfStreamingOutAndItsSettingsThisStreamWillBeDisplayedInTheDashboardIfYouChooseToUseHlsJpegOrMjpegThenYouCanConsumeTheStreamThroughOtherProgramsusingJpegStreamEssentiallyTurnsOffThePrimaryStreamAndUsesTheSnapshotBinToGetFrames": "<p>Esta seção irá designar o principal método de transmissão e suas configurações. Este fluxo será exibida no painel de controle. Se você escolher usar HLS, JPEG, ou MJPEG, em seguida, você pode consumir o fluxo através de outros programas.</p><p class=\"h_st_input h_st_jpeg\">Usando JPEG fluxo essencialmente desliga o fluxo principal e usa o instantâneo bin para obter quadros.</p>",
|
||||
"total": "total",
|
||||
"updateKeyText1": "\"updateKey\" faltando em \"conf.json\", não é possível atualizar dessa maneira até você adicionar",
|
||||
"updateKeyText2": "\"updateKey\" incorreta",
|
||||
"vaapi": "vaapi (VA-API)",
|
||||
"vda": "vda (Apple VDA Aceleração de Hardware)",
|
||||
"vdpau": "vdpau",
|
||||
"videotoolbox": "videotoolbox",
|
||||
"vp8_cuvid": "VP8 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"vp8_qsv": "VP8 (Quick Sync Vídeo)",
|
||||
"vp9_cuvid": "VP9 NVENC (NVIDIA HARDWARE de Aceleração)",
|
||||
"whenTheWidthAndHeightBoxesAreShownYouShouldSetThemTo640x480OrBelowThisWillOptimizeTheReadSpeedOfFrames\n": "<p>Quando as caixas Largura e Altura são mostradas, você deve configurá-los para 640x480 ou abaixo. Isto irá otimizar a velocidade de leitura dos quadros.</p>\n<p class=\"shinobi-detector-msg\"></p>",
|
||||
"years": "anos"
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"\"No Motion\" Detector": "\"Нет Движения\" Детектор",
|
||||
"# of Allow MJPEG Clients": "# of Allow MJPEG Clients <small>0 for infinite</small>",
|
||||
"\"No Motion\" Detector": "Детектор \"Нет Движения\"",
|
||||
"# of Allow MJPEG Clients": "Кол-во разрешённых MJPEG клиентов <small>0 для неограниченного</small>",
|
||||
"180 Degrees": "180 градусов",
|
||||
"2-Factor Authentication": "2-Факторной Аутентификации",
|
||||
"2-Factor Authentication": "Двухфакторная аутентификация",
|
||||
"90 Clockwise": "90 по часовой стрелке",
|
||||
"90 Clockwise and Vertical Flip": "90 по часовой стрелке и вертикальный флип",
|
||||
"90 Counter Clockwise and Vertical Flip (default)": "90 против часовой стрелки и по вертикали (по умолчанию)",
|
||||
"90 Clockwise and Vertical Flip": "90 по часовой стрелке и отражение по вертикали",
|
||||
"90 Counter Clockwise and Vertical Flip (default)": "90 против часовой стрелки и отражение по вертикали (по умолчанию)",
|
||||
"API": "API-интерфейс",
|
||||
"API Key Added": "Ключ API добавлены",
|
||||
"API Key Deleted": "API-ключ удален",
|
||||
"API Key Added": "API-ключ добавлен",
|
||||
"API Key Deleted": "API-ключ удалён",
|
||||
"API Keys": "Ключи API",
|
||||
"APIKeyAddedText": "Вы можете использовать этот ключ сейчас.",
|
||||
"APIKeyAddedText": "Ключ добалвен. Вы уже можете использовать этот ключ.",
|
||||
"APIKeyDeletedText": "Ключ был удален. Он больше не будет работать.",
|
||||
"ASC": "АСК",
|
||||
"Account Info": "Счета Информация",
|
||||
"AccountEditText1": "Не мог изменить. Обновить страницу, если проблема продолжается.",
|
||||
"Accounts": "Счетов",
|
||||
"ASC": "Возрастание",
|
||||
"Account Info": "Информация об аккаунте",
|
||||
"AccountEditText1": "Невозможно изменить. Обновить страницу, если проблема повторяется.",
|
||||
"Accounts": "Аккаунты",
|
||||
"Action for Selected": "Действие для выбранных",
|
||||
"Add": "Добавить",
|
||||
"Add Monitor": "Добавить Монитор",
|
||||
|
@ -26,424 +26,424 @@
|
|||
"All Monitors": "Все Мониторы",
|
||||
"All Monitors and Privileges": "Все мониторы и привилегии",
|
||||
"All Warnings": "Все Предупреждения",
|
||||
"Allow Next Command": "Разрешить следующей команды <small>в течение нескольких минут</small>",
|
||||
"Allow Next Email": "Разрешить следующей электронной почте <small>в течение нескольких минут</small>",
|
||||
"Allow Next Trigger": "Допустить следующего триггера <small>в Миллисекундах</small>",
|
||||
"Allowed IPs": "Допускается ИПС",
|
||||
"Analyzation Duration": "Продолжительность Анализа",
|
||||
"Allow Next Command": "Разрешить следующую команды <small>в минутах</small>",
|
||||
"Allow Next Email": "Разрешить следующую электронную почту <small>в минутах</small>",
|
||||
"Allow Next Trigger": "Разрешить следующий триггер <small>в миллисекундах</small>",
|
||||
"Allowed IPs": "Допустимые IP-адреса",
|
||||
"Analyzation Duration": "Продолжительность анализа",
|
||||
"Archive": "Архив",
|
||||
"Audio Codec": "Аудио Кодек",
|
||||
"Audio Codec": "Аудио-кодек",
|
||||
"Authenticate": "Аутентифицировать",
|
||||
"Auto": "Авто",
|
||||
"Autosave": "Автосохранение",
|
||||
"Base64 over Websocket": "В base64 через websocket",
|
||||
"Base64 over Websocket": "Base64 через websocket",
|
||||
"Bottom Left": "Внизу Слева",
|
||||
"Bottom Right": "Нижний Правый",
|
||||
"Browser Console Log": "Консоли Браузера Войти",
|
||||
"CPU": "Процессор",
|
||||
"CPU indicator will not work. Continuing...": "Индикатор процессора не будет работать. Продолжение...",
|
||||
"CSS": "В CSS <small>Стиль вашей приборной панели.</small>",
|
||||
"Bottom Right": "Внизу Справа",
|
||||
"Browser Console Log": "Журнал консоли браузера",
|
||||
"CPU": "ЦП",
|
||||
"CPU indicator will not work. Continuing...": "Индикатор ЦП не будет работать. Продолжение...",
|
||||
"CSS": "CSS <small>Стиль вашей приборной панели.</small>",
|
||||
"Calendar": "Календарь",
|
||||
"Camera Password": "Пароль Камеры",
|
||||
"Camera Username": "Имя Пользователя Камеры",
|
||||
"Camera Password": "Пароль камеры",
|
||||
"Camera Username": "Логин камеры",
|
||||
"Camera is not recording": "Камера не записывает",
|
||||
"CameraNotRecordingText": "Параметры могут быть несовместимы. Проверить датчики. Перезапуск...",
|
||||
"Can Control Monitors": "Может Управлять Мониторами",
|
||||
"Can Delete Videos": "Можно Удалять Видео",
|
||||
"Can Delete Videos and Events": "Можно удалять видео и событий",
|
||||
"Can Edit Monitor": "Можете Редактировать Монитор",
|
||||
"Can Get Logs": "Можете Сделать Логи",
|
||||
"Can Get Monitors": "Можете Сделать Мониторы",
|
||||
"Can View Monitor": "Можно Посмотреть Монитор",
|
||||
"Can View Snapshots": "Можете Посмотреть Снимки",
|
||||
"Can View Streams": "Можете Посмотреть Потоков",
|
||||
"Can View Videos": "Можете Посмотреть Видео",
|
||||
"Can View Videos and Events": "Можете посмотреть видео и событий",
|
||||
"Can't Connect": "Не могу подключиться",
|
||||
"CameraNotRecordingText": "Параметры могут быть несовместимы. Проверьте кодировку потока. Перезапуск...",
|
||||
"Can Control Monitors": "Может управлять мониторами",
|
||||
"Can Delete Videos": "Может удалять видео",
|
||||
"Can Delete Videos and Events": "Может удалять видео и события",
|
||||
"Can Edit Monitor": "Может редактировать мониторы",
|
||||
"Can Get Logs": "Может получать журнал",
|
||||
"Can Get Monitors": "Может получать мониторы",
|
||||
"Can View Monitor": "Может смотреть мониторы",
|
||||
"Can View Snapshots": "Может смотрет снимки",
|
||||
"Can View Streams": "Может смотреть потоки",
|
||||
"Can View Videos": "Может смотреть видео",
|
||||
"Can View Videos and Events": "Может смотреть видео и события",
|
||||
"Can't Connect": "Не удалось подключиться",
|
||||
"Center": "Центр <small>URL-Адрес</small>",
|
||||
"Chat on Discord": "Чат на раздор",
|
||||
"Chat on Discord": "Чат в Discord",
|
||||
"Check": "Проверить",
|
||||
"Check Signal Interval": "Проверка сигнала интервал <small>в минутах</small>",
|
||||
"Check for Motion First": "Проверить в первую очередь для движения",
|
||||
"Check Signal Interval": "Интервал проверки сигнала <small>в минутах</small>",
|
||||
"Check for Motion First": "Сначала проверить движение",
|
||||
"Close": "Закрыть",
|
||||
"Closed": "Закрытые",
|
||||
"Command": "Команда",
|
||||
"Command on Trigger": "Команду на триггер",
|
||||
"Complete Stream URL": "Полный URL-адрес потока",
|
||||
"Command on Trigger": "Команда на триггер",
|
||||
"Complete Stream URL": "Полный URL потока",
|
||||
"Confirm": "Подтвердить",
|
||||
"Connected": "Подключен",
|
||||
"Connection Type": "Тип Подключения",
|
||||
"Control": "Контроль",
|
||||
"Control Error": "Ошибка Контроля",
|
||||
"ControlErrorText1": "Контроль не включен",
|
||||
"Controllable": "Управляема",
|
||||
"Country of Plates": "Страна пластин",
|
||||
"Counts of Motion": "Подсчитывает движения",
|
||||
"Current": "Тока",
|
||||
"Currently viewing": "В настоящее время просмотр",
|
||||
"Custom": "Заказ",
|
||||
"Custom Base URL": "Базовый пользовательский URL-адрес <small>оставить пустым, чтобы использовать URL-адрес узла</small>",
|
||||
"DB Lost.. Retrying..": "База Данных Потеряла.. Повтор..",
|
||||
"DESC": "Описание",
|
||||
"Control": "Управление",
|
||||
"Control Error": "Ошибка управления",
|
||||
"ControlErrorText1": "Управление не включено",
|
||||
"Controllable": "Управляемая",
|
||||
"Country of Plates": "Страна номерного знака",
|
||||
"Counts of Motion": "Количество движений",
|
||||
"Current": "Текущий",
|
||||
"Currently viewing": "Сейчас просматривают",
|
||||
"Custom": "Пользовательский",
|
||||
"Custom Base URL": "Базовый пользовательский URL <small>оставьте пустым, чтобы использовать URL узла</small>",
|
||||
"DB Lost.. Retrying..": "Подключение к БД утеряно. Повтор...",
|
||||
"DESC": "Убывание",
|
||||
"Dashboard": "Приборной панели",
|
||||
"Dashboard Language": "Язык Приборной Панели",
|
||||
"Dashcam": "Автомобильный видеорегистратор",
|
||||
"Dashcam (Streamer v2)": "Автомобильный видеорегистратор (Стример П2)",
|
||||
"Date Range": "Диапазон Дат",
|
||||
"Debug": "Отлаживать",
|
||||
"Dashcam": "Видеорегистратор",
|
||||
"Dashcam (Streamer v2)": "Видеорегистратор (Streamer v2)",
|
||||
"Date Range": "Диапазон дат",
|
||||
"Debug": "Отладка",
|
||||
"Default": "По умолчанию",
|
||||
"Delete": "Удалить",
|
||||
"Delete Filter": "Удалить Фильтр",
|
||||
"Delete Matches": "Удалить Играм",
|
||||
"Delete Monitor": "Удалить Монитор",
|
||||
"Delete Motionless Video": "Удалить Недвижно Видео",
|
||||
"Delete Motionless Videos (Record)": "Удалить Неподвижными Видео (Запись)",
|
||||
"Delete Selected Videos": "Удаление Выбранного Видео",
|
||||
"Delete Video": "Удалить Видео",
|
||||
"Delete Filter": "Удалить фильтр",
|
||||
"Delete Matches": "Удалить совпадения",
|
||||
"Delete Monitor": "Удалить монитор",
|
||||
"Delete Motionless Video": "Удалить видео без движения",
|
||||
"Delete Motionless Videos (Record)": "Удалить видео без движения (запись)",
|
||||
"Delete Selected Videos": "Удалить выбранные видео",
|
||||
"Delete Video": "Удалить видео",
|
||||
"Delete selected": "Удалить выбранные",
|
||||
"DeleteMonitorText": "Вы действительно хотите удалить этот монитор? Нельзя восстановить. Файлы для этот идентификатор будет оставаться в файловой системе. Если вы решили воссоздать монитора с одинаковым кодом видео и события станут видны в личном кабинете.",
|
||||
"DeleteSelectedVideosMsg": "Вы действительно хотите удалить эти видео? Вы не сможете восстановить их.",
|
||||
"DeleteVideoMsg": "Вы действительно хотите удалить это видео? Нельзя восстановить.",
|
||||
"DeleteMonitorText": "Вы действительно хотите удалить этот монитор? Это действие нельзя отменить. Файлы для этого ID останутся в файловой системе. Если позже вы создадите монитор с таким-же ID, события станут видны в личном кабинете.",
|
||||
"DeleteSelectedVideosMsg": "Вы действительно хотите удалить эти видео? Это действие нельзя отменить.",
|
||||
"DeleteVideoMsg": "Вы действительно хотите удалить это видео? Это действие нельзя отменить.",
|
||||
"Deleted": "Удален",
|
||||
"Detect Objects": "Обнаруживать объекты <small class=\"\">ниже</small>",
|
||||
"Detector": "Детектор",
|
||||
"Detector Flags": "Флаги Детектор",
|
||||
"Detector Rate": "Детектор скорости <small>(ФПС)</small>",
|
||||
"DetectorText": "<p>Когда показана Ширина и Высота коробки, вы должны установить их до 640х480 или ниже. Это позволит оптимизировать скорость чтения кадров.</p>",
|
||||
"Disable Night Vision": "Отключить ночное видение <small>URL-Адрес</small>",
|
||||
"Disable Nightvision": "Отключение Ночного Видения",
|
||||
"Detector Flags": "Флаги детектора",
|
||||
"Detector Rate": "Скорость детектора <small>(кадры в секунду)</small>",
|
||||
"DetectorText": "<p>Когда показана ширина и высота области, установите её размер в 640х480 или меньше. Это позволит оптимизировать скорость чтения кадров.</p>",
|
||||
"Disable Night Vision": "Отключение ночного видения <small>URL</small>",
|
||||
"Disable Nightvision": "Отключить ночное видение",
|
||||
"Disabled": "Отключен",
|
||||
"Documentation": "Документация",
|
||||
"Don't show this anymore": "Больше не показывать это",
|
||||
"Double Quote Directory": "Двойная Кавычка каталога <small>у некоторых каталогов есть пробелы. С помощью этого может произойти сбой некоторых камер.</small>",
|
||||
"Down": "Вниз <small>URL-Адрес</small>",
|
||||
"Down Stop": "Вниз стоп <small>URL-Адрес</small>",
|
||||
"Double Quote Directory": "Двойные кавычки в имени каталога <small>у некоторых каталогов есть пробелы в имени. Из-за этого может произойти сбой некоторых камер.</small>",
|
||||
"Down": "Движение вниз <small>URL</small>",
|
||||
"Down Stop": "Остановка движения вниз <small>URL</small>",
|
||||
"Download": "Скачать",
|
||||
"EU": "ЕС",
|
||||
"EU": "Европа",
|
||||
"Edit": "Редактировать",
|
||||
"Email": "Электронной почты",
|
||||
"Email Details": "Подробности По Электронной Почте",
|
||||
"Email on No Motion": "Электронной почты на \"нет движения\"",
|
||||
"Email on Trigger": "Электронной почты на Триггерных <small>писем перейти к главной владельца счета адрес для входа.</small>",
|
||||
"Enable Night Vision": "Включение ночного видения <small>URL-Адрес</small>",
|
||||
"Enable Nightvision": "Включение Ночного Видения",
|
||||
"Email": "Электронная почта",
|
||||
"Email Details": "Детали на электронную почту",
|
||||
"Email on No Motion": "Письмо при отсутствии движения",
|
||||
"Email on Trigger": "Письмо при срабатывании триггера <small>письма будут приходить на почту, утсановленную как логин для аккаунта</small>",
|
||||
"Enable Night Vision": "Включение ночного видения <small>URL</small>",
|
||||
"Enable Nightvision": "Включение ночного видения",
|
||||
"Enabled": "Включен",
|
||||
"End": "Конец",
|
||||
"End Time": "Время Окончания",
|
||||
"End Time": "Время окончания",
|
||||
"Ended": "Закончился",
|
||||
"Enlarge": "Увеличить",
|
||||
"Enter this code to proceed": "Введите этот код, чтобы продолжить",
|
||||
"Equal to": "Равна",
|
||||
"Error Connecting": "Ошибка Подключения",
|
||||
"Equal to": "Равно",
|
||||
"Error Connecting": "Ошибка подключения",
|
||||
"Event": "Событие",
|
||||
"Event Limit": "Лимит Событие",
|
||||
"EventText1": "Срабатывает событие движения в",
|
||||
"EventText2": "Не мог изображения по электронной почте, файл не был доступен",
|
||||
"Event Limit": "Ограничение событий",
|
||||
"EventText1": "Сработало событие движения в",
|
||||
"EventText2": "Не удалось отправить изображение по электронной почте - файл не был доступен",
|
||||
"Events": "События",
|
||||
"Example": "Пример",
|
||||
"Execute Command": "Выполнить Команду",
|
||||
"Execute Command": "Выполнить команду",
|
||||
"Executed": "Выполнен",
|
||||
"Export": "Экспорт",
|
||||
"FFmpegCantStart": "Видео не смог запустить",
|
||||
"FFmpegCantStartText": "Запись двигатель для этой камеры не удалось запустить. Там может быть что-то не так с настройкой камеры. Если есть какие-либо другие журналы, чем этот, пожалуйста, пишите в <b>вопросы</b> на github.",
|
||||
"FFmpegTip": "FFprobe-простой мультимедийный анализатор потоков. Вы можете использовать его для вывода всевозможной информации о входных данных, включая Продолжительность, частоту кадров, Размер кадра и т. д.",
|
||||
"FFprobe": "Зонд",
|
||||
"FactorAuthText1": "Код будет активна только в течение 15 минут. Если вы войти снова, Таймер будет сброшен до 15 минут с тем же кодом.",
|
||||
"Fatal": "Роковой",
|
||||
"Fatal Maximum Reached": "Роковой Максимальным Достигнутым, Останавливая Камеру.",
|
||||
"FatalMaximumReachedText": "Ошибка JPEG был смертельным.",
|
||||
"Feed-in Image Height": "Кормить-в Высота изображения",
|
||||
"Feed-in Image Width": "Кормить-в изображения Ширина",
|
||||
"FFmpegCantStart": "Не удалось запустить FFmpeg",
|
||||
"FFmpegCantStartText": "Ядро записи для этой камеры не удалось запустить. Это может быть вызвано ошибочной настройкой камеры. Если, помимо этой, есть другие записи в журнале, пожалуйста, пришлите их в <a href=\"https://gitlab.com/Shinobi-Systems/Shinobi/issues\" target=\"_blank\"><b>Issues</b> на GitLab</a>.",
|
||||
"FFmpegTip": "FFprobe - это простой анализатор мултимедийных потоков. Вы можете использовать его для вывода всевозможной информации о потоке, включая продолжительность, частоту кадров, размер кадра и т. д.",
|
||||
"FFprobe": "Зонд FFprobe",
|
||||
"FactorAuthText1": "Код будет активен только в течение 15 минут. Если вы войдёте снова, таймер будет сброшен до 15 минут с тем же кодом.",
|
||||
"Fatal": "Фатальная",
|
||||
"Fatal Maximum Reached": "Фатальный максимум достигнут, камера останавливается.",
|
||||
"FatalMaximumReachedText": "Ошибка JPEG была фатальной.",
|
||||
"Feed-in Image Height": "Входная высота изображения",
|
||||
"Feed-in Image Width": "Входная ширина изображения",
|
||||
"Fields cannot be empty": "Поля не могут быть пустыми",
|
||||
"File Not Exist": "Файл Не Существует",
|
||||
"File Not Found": "Файл Не Найден",
|
||||
"File Type": "Тип Файла",
|
||||
"FileNotExistText": "Не удается сохранить файл не существует. Что-то пошло не так.",
|
||||
"Filename": "Именем",
|
||||
"Filesize": "Размер",
|
||||
"Filter ID": "Идентификатор фильтра",
|
||||
"Filter Matches": "Фильтра",
|
||||
"Filter Name": "Название Фильтра",
|
||||
"FilterMatchesText1": "Этот фильтр имеет условия.",
|
||||
"FilterMatchesText2": "видео нашли.",
|
||||
"File Not Exist": "Файл не существует",
|
||||
"File Not Found": "Файл не найден",
|
||||
"File Type": "Тип файла",
|
||||
"FileNotExistText": "Невозможно сохранить несуществущий файл. Что-то пошло не так.",
|
||||
"Filename": "Имя файла",
|
||||
"Filesize": "Размер файла",
|
||||
"Filter ID": "ID фильтра",
|
||||
"Filter Matches": "Совпадение фильтра",
|
||||
"Filter Name": "Название фильтра",
|
||||
"FilterMatchesText1": "Этот фильтр удовлетворил условия.",
|
||||
"FilterMatchesText2": "видео найдено.",
|
||||
"Filters": "Фильтры",
|
||||
"Filters Updated": "Фильтры Обновлены",
|
||||
"Filters Updated": "Фильтры обновлены",
|
||||
"FiltersUpdatedText": "Ваши изменения были сохранены и применены.",
|
||||
"Find Where": "Найти Где",
|
||||
"Find Where": "Найти где",
|
||||
"Fix": "Исправить",
|
||||
"Fix Video": "Исправить Видео",
|
||||
"FixVideoMsg": "Вы хотите, чтобы удалить это видео? Вы не можете отменить это действие.",
|
||||
"Font Path": "Путь Шрифта",
|
||||
"Font Size": "Размер Шрифта",
|
||||
"Force Port": "Сила Порта",
|
||||
"Found Devices": "Найденные Устройства",
|
||||
"Fix Video": "Исправить видео",
|
||||
"FixVideoMsg": "Вы действительно хотите исправить это видео? Это действие нельзя отменить.",
|
||||
"Font Path": "Путь к шрифту",
|
||||
"Font Size": "Размер шрифта",
|
||||
"Force Port": "Порт (принудительно)",
|
||||
"Found Devices": "Найдено устройств",
|
||||
"Frame Rate": "Частота кадров <small>(кадров / с)</small>",
|
||||
"Full Frame Detection": "Полное Обнаружение Кадра",
|
||||
"Full Frame Detection": "Обнаружение в полном Кадре",
|
||||
"Fullscreen": "Полноэкранный",
|
||||
"Greater Than": "Больше",
|
||||
"Greater Than or Equal to": "Больше или равна",
|
||||
"Group Key": "Ключевые Группы",
|
||||
"Group Name": "Название Группы",
|
||||
"Greater Than or Equal to": "Больше или равно",
|
||||
"Group Key": "Ключ группы",
|
||||
"Group Name": "Название группы",
|
||||
"Grouping": "Группировка ",
|
||||
"H.264 / H.265 / H.265+": "Формат H. 264 / H. 265 / H. 265 В ",
|
||||
"HLS (.m3u8)": "ЗОЖ (.m3u8 форматов)",
|
||||
"HLS (includes Audio)": "ЗОЖ (включает Аудио)",
|
||||
"HLS Audio Encoder": "ЗОЖ Аудио Шифратор",
|
||||
"HLS List Size": "Размер списка ЗОЖ",
|
||||
"HLS Preset": "ЗОЖ предустановленных",
|
||||
"HLS Segment Length": "ЗОЖ сегмент длиной <small>в секунд</small>",
|
||||
"HLS Video Encoder": "ЗОЖ видео Энкодера",
|
||||
"H.264 / H.265 / H.265+": "Формат H.264 / H.265 / H.265+",
|
||||
"HLS (.m3u8)": "Формат HLS (.m3u8)",
|
||||
"HLS (includes Audio)": "Формат HLS со звуком",
|
||||
"HLS Audio Encoder": "HLS Аудио кодировщик",
|
||||
"HLS List Size": "Размер списка HLS",
|
||||
"HLS Preset": "Предустановка HLS",
|
||||
"HLS Segment Length": "Длина сегмента HLS <small>в секундах</small>",
|
||||
"HLS Video Encoder": "HLS Видео кодировщик",
|
||||
"HTTP": "Протокол http",
|
||||
"HTTPS": "Протоколу https",
|
||||
"Height": "Высота",
|
||||
"Help": "Помогите",
|
||||
"Hide List": "Скрыть Список",
|
||||
"Hide Notes": "Скрыть Примечания",
|
||||
"Host": "Хозяин",
|
||||
"Hotswap Modes (Watch-Only)": "Режима Горячей Замены (Часы Только)",
|
||||
"Help": "Помощь",
|
||||
"Hide List": "Скрыть список",
|
||||
"Hide Notes": "Скрыть заметки",
|
||||
"Host": "Хост",
|
||||
"Hotswap Modes (Watch-Only)": "Режим горячей замены (только просмотр)",
|
||||
"How to Record": "Как записать",
|
||||
"IP Address": "IP-адрес",
|
||||
"Identity": "Личность",
|
||||
"IdentityText1": "Это как система будет определять данные для этого потока. Вы не можете изменить <b>идентификатор монитора</b> после того, как вы нажали \"сохранить\". Если вы хотите, вы можете сделать <b>монитор код</b> более читабельным, прежде чем продолжить.",
|
||||
"IdentityText2": "Вы можете дублировать монитор, изменяя <b>идентификатор монитора</b> нажмите клавишу сохранить. Вы <b>не можете</b> использовать идентификатор монитора, который уже существует или он сохранит за монитора базы данных.",
|
||||
"Idle": "Праздный",
|
||||
"Image Height": "Высота Изображения",
|
||||
"Image Location": "Изображения Расположение <small>абсолютный путь или оставьте поле пустым, чтобы использовать глобальные</small>",
|
||||
"Image Position": "Положение Изображения",
|
||||
"Image Width": "Ширина Изображения",
|
||||
"Identity": "Идентификация",
|
||||
"IdentityText1": "Здесь определяется, как система будет идентифийироват данные этого потока. После сохранения вы не сможете изменить <b>ID монитора</b>. Если хотите, можете сделать <b>ID монитора</b> понятным для людей перед прожолжением.",
|
||||
"IdentityText2": "Вы можете сделать дубликат монитора, изменив <b>ID монитора</b> и нажав \"Сохранить\". <b>Запрещено</b> использовать ID существующего монитора - в противном случае его информация в БД будет перезаписана.",
|
||||
"Idle": "Простаивающий",
|
||||
"Image Height": "Высота изображения",
|
||||
"Image Location": "Путь к изображению <small>абсолютный путь к файлу, или оставьте поле пустым для использования глобального</small>",
|
||||
"Image Position": "Положение изображения",
|
||||
"Image Width": "Ширина изображения",
|
||||
"Import": "Импорт",
|
||||
"Import Monitor Configuration": "Импорт Конфигурации Монитора",
|
||||
"ImportMonitorConfigurationText": "Это overrwrite любые изменения в настоящее время не сохранены. Импортированные изменения будут применены только при нажатии <b>сохранить</b>.",
|
||||
"Import Monitor Configuration": "Импорт конфигурации монитора",
|
||||
"ImportMonitorConfigurationText": "Это перезапишет любые не сохранённые изменения. Импортированные настройки будут применены только при нажатии <b>Сохранить</b>.",
|
||||
"In": "В",
|
||||
"Incorrect Settings Chosen": "Выбраны Неправильные Параметры",
|
||||
"Indifference": "Равнодушие",
|
||||
"Input": "Вход",
|
||||
"Input Flags": "Входных Флагов",
|
||||
"Input Type": "Тип Входного Сигнала",
|
||||
"InputText1": "Этот раздел рассказывает шиноби, как использовать поток. Для оптимальной производительности, попробуйте настройки внутренних параметров камеры. Найти следующие варианты и установить их, как показано. Чтобы найти вашу камеру, вы можете использовать <b>встроенный onvif и сканер</b> шиноби. Некоторые камеры onvif требуют использования инструмента управления, чтобы изменить свои внутренние настройки. Если Вы не можете найти вашей камеры вы можете попробовать <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">onvif и диспетчер устройств для Windows</a>.",
|
||||
"InputText2": "<ul><li><b>Кадров (fps) :</b> высокая : 10 - 15 ФПС, минимум : 2-5 ФПС</li><li><b>I-кадр интервал :</b> 80</li><li><b>Тип битрейта :</b> CBR (Постоянный Битрейт)</li><li><b>Битрейт :</b> между 256кбит / с - 500 Кбит / с</li></ul>",
|
||||
"InputText3": "Если вам нужна помощь в выяснение того, что входной тип Вашей камеры вы можете посмотреть в <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">камеры URL-адреса в списке</a> на шиноби сайт.",
|
||||
"Invalid JSON": "Недопустимый Формат json",
|
||||
"InvalidJSONText": "Пожалуйста, убедитесь, что это действительный json-строку для конфигурации шиноби монитор.",
|
||||
"Incorrect Settings Chosen": "Выбраны неправильные установки",
|
||||
"Indifference": "Безразличие",
|
||||
"Input": "Ввод",
|
||||
"Input Flags": "Флаги ввода",
|
||||
"Input Type": "Тип ввода",
|
||||
"InputText1": "Этот раздел описывает, как использовать поток. Для оптимальной производительности, измените внутренние настройки камеры. Найдите соответствующие настройки и установите их, как показано ниже. Чтобы обнаружить вашу камеру, вы можете использовать <b>встроенный ONVIF-сканер</b>. Некоторые ONVIF камеры требуют использования специального инструмента управления для изменения внутренних настроек. Если Вы не можете обнаружить ваши, попробуйте <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">ONVIF Device Manager для Windows</a>.",
|
||||
"InputText2": "<ul><li><b>Кадров в секунду (FPS) :</b>10-15 FPS, минимум 2-5 FPS</li><li><b>Интервал I-кадра:</b> 80</li><li><b>Тип битрейта:</b> CBR (постоянный)</li><li><b>Битрейт: </b> от 256Кбит/с до 500 Кбит/с</li></ul>",
|
||||
"InputText3": "Если вам нужна помощь в определении потока вашей камеры, обратитесь к странице <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Списка URL-адресов камер</a> на сайте Shinobi.",
|
||||
"Invalid JSON": "Недопустимый формат JSON",
|
||||
"InvalidJSONText": "Пожалуйста, убедитесь, что для конфигурации монитора указана корректная JSON-строка.",
|
||||
"JPEG": "В формате JPEG",
|
||||
"JPEG (Auto Enables JPEG API)": "В формате JPEG (авто позволит использовать API в формате JPEG)",
|
||||
"JPEG API": "В формате JPEG интерфейс API <small>снимка (цги-бин)</small>",
|
||||
"JPEG Error": "Ошибка формата JPEG",
|
||||
"JPEG Mode": "JPEG режим",
|
||||
"JPEGErrorText": "Возникла проблема получения данных с камеры.",
|
||||
"Leave blank for random.": "Оставьте пустым для случайных.",
|
||||
"Left": "Левый <small>Адрес</small>",
|
||||
"Left Stop": "Левый стоп <small>URL-Адрес</small>",
|
||||
"JPEG (Auto Enables JPEG API)": "JPEG ('Авто'' включает JPEG API)",
|
||||
"JPEG API": "JPEG API <small>cgi-bin</small>",
|
||||
"JPEG Error": "Ошибка JPEG",
|
||||
"JPEG Mode": "Режим JPEG",
|
||||
"JPEGErrorText": "Ошибка получения JPEG данных с камеры.",
|
||||
"Leave blank for random.": "Оставьте пустым для случайного значения.",
|
||||
"Left": "Влево <small>URL</small>",
|
||||
"Left Stop": "Остановка влево <small>URL</small>",
|
||||
"Less Than": "Меньше",
|
||||
"Less Than or Equal to": "Меньше или равно",
|
||||
"Like": "Как",
|
||||
"Lisence Plate Detector": "Лицензии Детектор Пластины",
|
||||
"List Toggle": "Список Тумблер",
|
||||
"Live Stream Toggle": "Жить Переключения Потока",
|
||||
"Live View": "Живой Просмотр",
|
||||
"Local": "Местные",
|
||||
"Log Level": "Уровень Журнала",
|
||||
"Log Signal Event": "Журнал сигнал события <small>на стороне клиента только</small>",
|
||||
"Logging": "Лесозаготовки",
|
||||
"Like": "Наподобие",
|
||||
"Lisence Plate Detector": "Распознавание номерных знаков",
|
||||
"List Toggle": "Переключение списка",
|
||||
"Live Stream Toggle": "Переключение живого вида",
|
||||
"Live View": "Живой вид",
|
||||
"Local": "Локальный",
|
||||
"Log Level": "Уровень журналирования",
|
||||
"Log Signal Event": "Журналировать сигнальные события <small>только на стороне клиента</small>",
|
||||
"Logging": "Журналирование",
|
||||
"Login": "Логин",
|
||||
"Logout": "Выход",
|
||||
"Logs": "Журналы",
|
||||
"MB": "МБ",
|
||||
"MB": "Мб",
|
||||
"MJPEG": "Формат MJPEG",
|
||||
"MP4 (copy, libx264, libx265)": "МР4 (копия, поддержкой libx264, libx265)",
|
||||
"MPEG-4 (.mp4 / .ts)": "Формат MPEG-4 (.МР4 / .ТС)",
|
||||
"MailError": "Mail ошибка : не удалось отправить письмо, проверьте конф.в json. Пропуская какие-либо функции, опираясь на рассылки.",
|
||||
"Matches": "Матчи",
|
||||
"Max Storage Amount": "Памяти Макс. объем <small>в мегабайтах</small>",
|
||||
"MP4 (copy, libx264, libx265)": "Формат MP4 (копирование, libx264, libx265)",
|
||||
"MPEG-4 (.mp4 / .ts)": "Формат MPEG-4 (.mp4 / .ts)",
|
||||
"MailError": "Ошибка электронной почты: не удалось отправить письмо, проверьте настройки в conf.json.",
|
||||
"Matches": "Совпадения",
|
||||
"Max Storage Amount": "Максимально занимаемое место <small>в мегабайтах</small>",
|
||||
"Mode": "Режим",
|
||||
"Monitor": "Монитор",
|
||||
"Monitor Added by user": "Монитор, добавленные пользователем.",
|
||||
"Monitor Capture Rate": "Монитор скорости захвата <small>(ФПС)</small>",
|
||||
"Monitor Groups": "Мониторинг Групп",
|
||||
"Monitor ID": "Идентификатор монитора",
|
||||
"Monitor Idling": "Монитор Простаивания",
|
||||
"Monitor Name": "Имя Монитора",
|
||||
"Monitor Settings": "Настройки Монитора",
|
||||
"Monitor Stopped": "Монитор Перестал",
|
||||
"Monitor Updated by user": "Монитор обновляться пользователем.",
|
||||
"Monitor mode changed": "Режим монитора меняется",
|
||||
"Monitor mode is already": "Режим монитора уже",
|
||||
"Monitor Added by user": "Монитор добавлен пользователем.",
|
||||
"Monitor Capture Rate": "Скорость захвата монитора<small>(FPS)</small>",
|
||||
"Monitor Groups": "Группы мониторов",
|
||||
"Monitor ID": "ID монитора",
|
||||
"Monitor Idling": "Простаивание монитора",
|
||||
"Monitor Name": "Имя монитора",
|
||||
"Monitor Settings": "Настройки монитора",
|
||||
"Monitor Stopped": "Монитор остановился",
|
||||
"Monitor Updated by user": "Монитор обновлён пользователем.",
|
||||
"Monitor mode changed": "Изменён режим монитора",
|
||||
"Monitor mode is already": "Режим монитора уже установлен в",
|
||||
"Monitor or Key does not exist.": "Монитор или ключ не существует.",
|
||||
"MonitorIdlingText": "Монитор сессии был заказан на холостом ходу.",
|
||||
"MonitorStoppedText": "Монитор сеансов было приказано прекратить.",
|
||||
"MonitorIdlingText": "Сессия монитора изменена на простаивающую.",
|
||||
"MonitorStoppedText": "Сессия монитора остановлена.",
|
||||
"Monitors": "Мониторы",
|
||||
"Monitors per row": "Мониторы на строку <small>для монтажа</small>",
|
||||
"Montage": "Фотомонтаж",
|
||||
"Motion GUI": "ГУЙ движения",
|
||||
"Motion Meter": "Метр Движения",
|
||||
"Monitors per row": "Мониторов на строку <small>для монтажа</small>",
|
||||
"Montage": "Монтаж",
|
||||
"Motion GUI": "Интерфейс движения",
|
||||
"Motion Meter": "Шкала движения",
|
||||
"Name": "Название",
|
||||
"No": "Нет",
|
||||
"No Audio": "Нет Звука",
|
||||
"No Data": "Нет Данных",
|
||||
"No Events found for this video": "Не для этого видео",
|
||||
"No Group with this key exists": "Ни одна группа с таким ключом существует",
|
||||
"No Monitor Found, Ignoring Request": "Нет Монитор Нашли, Игнорируя Просьбу",
|
||||
"No Rotation": "Нет Вращения",
|
||||
"No Audio": "Без звука",
|
||||
"No Data": "Нет данных",
|
||||
"No Events found for this video": "Для этого видео событий не найдено",
|
||||
"No Group with this key exists": "Групп с таким ключом не существует",
|
||||
"No Monitor Found, Ignoring Request": "Не найден монитор Нашли, запрос проигнорирован",
|
||||
"No Rotation": "Без вращения",
|
||||
"No such file": "Нет такого файла",
|
||||
"NoMotionEmailText1": "Нет движения",
|
||||
"NoMotionEmailText2": "Не было никакого движения на камеру",
|
||||
"NoVideosFoundForDateRange": "Не нашла видео в этот диапазон дат. Попробуйте установить дату начала дальше.",
|
||||
"Not Authorized": "Не Разрешено",
|
||||
"Not Connected": "Не Подключен",
|
||||
"Not Equal to": "Не равны",
|
||||
"Not In": "Не В",
|
||||
"Not Matches": "Не Соответствует",
|
||||
"Not Permitted": "Не Допускается",
|
||||
"Not an Administrator Account": "Не является учетной записью администратора",
|
||||
"NotAuthorizedText1": "Не разрешенные, представить команду init с \"авт\",\"ке\" и \"юид\"",
|
||||
"NoMotionEmailText2": "Не было никакого движения в камере",
|
||||
"NoVideosFoundForDateRange": "Не найдено видео в указанном промежутке. Попробуйте расширить диапазон дат.",
|
||||
"Not Authorized": "Не разрешено",
|
||||
"Not Connected": "Не подключен",
|
||||
"Not Equal to": "Не равно",
|
||||
"Not In": "Не входит",
|
||||
"Not Matches": "Не соответствует",
|
||||
"Not Permitted": "Не допускается",
|
||||
"Not an Administrator Account": "Не является аккаунтом администратора",
|
||||
"NotAuthorizedText1": "Не разрешено, подайте команду init с \"auth\",\"ke\", and \"uid\"",
|
||||
"Notes": "Примечания",
|
||||
"NotesPlacholder": "Комментарии Вы хотите оставить на этом настройки камеры.",
|
||||
"NotesPlacholder": "Комментарии для этих настроек камеры.",
|
||||
"Number of Days to keep": "Количество дней хранения",
|
||||
"ONVIF": "Стандарт onvif",
|
||||
"ONVIF Scanner": "Сканер стандарта onvif",
|
||||
"ONVIFnote": "Откройте стандарт onvif устройств за пределами вашей сети или оставьте его пустым для сканирования сети. <br>Имя пользователя и пароль можно оставить пустым.",
|
||||
"OpenCV Cascades": "В Формате Opencv Каскада",
|
||||
"Order Streams": "Потоки Заказов",
|
||||
"Output Method": "Способ Вывода",
|
||||
"ONVIF": "Стандарт ONVIF",
|
||||
"ONVIF Scanner": "Сканер ONVIF",
|
||||
"ONVIFnote": "Обнаружте ONVIF-совместимые в сетях за пределами вашей, или оставьте поле пустым для сканирования вашей текущей сети. <br>Логин и пароль можно оставить пустым.",
|
||||
"OpenCV Cascades": "Каскады OpenCV",
|
||||
"Order Streams": "Порядок потоков",
|
||||
"Output Method": "Способ вывода",
|
||||
"Password": "Пароль",
|
||||
"Password Again": "Пароль Еще Раз",
|
||||
"Password Again": "Пароль ещё раз",
|
||||
"Passwords don't match": "Пароли не совпадают",
|
||||
"Paste JSON here.": "Вставить json здесь.",
|
||||
"Paste JSON here.": "Вставьте JSNON здесь.",
|
||||
"Path": "Путь",
|
||||
"Permissions": "Разрешения",
|
||||
"Points": "Точки <small>при добавлении точек, щелкните на краю полигона.</small>",
|
||||
"Points": "Точки <small>для добавления точки, щелкните на границе фигуры.</small>",
|
||||
"Port": "Порт",
|
||||
"Position X": "Положение X",
|
||||
"Position Y": "Позиция Y",
|
||||
"Power Video Viewer": "Питания Видео Просмотра",
|
||||
"Power Viewer": "Зритель Власти",
|
||||
"Position X": "Положение по X",
|
||||
"Position Y": "Положение по Y",
|
||||
"Power Video Viewer": "Продвинутый просмотр видео",
|
||||
"Power Viewer": "Продвинутый просмотр",
|
||||
"Preferences": "Предпочтения",
|
||||
"Preset": "Предустановленные",
|
||||
"Probe Size": "Размер Зонда",
|
||||
"Process Crashed for Monitor": "Процесс разбился на монитор",
|
||||
"Process Unexpected Exit": "Процесс Неожиданный Выход",
|
||||
"Preset": "Предустановка",
|
||||
"Probe Size": "Размер зонда",
|
||||
"Process Crashed for Monitor": "Процесс завершился аварийно для монитора",
|
||||
"Process Unexpected Exit": "Неожиданное завершение процесса",
|
||||
"Profile": "Профиль",
|
||||
"Quality": "Качество <small>1 высота, 23 низкий</small>",
|
||||
"Quality": "Качество <small>1 - высокий, 23 - низкий</small>",
|
||||
"Query": "Запрос",
|
||||
"RAM": "Оперативной памяти",
|
||||
"RAM": "ОЗУ",
|
||||
"RTSP": "Протокол RTSP",
|
||||
"RTSP Transport": "РЦП транспорта",
|
||||
"Range or Single": "Диапазон или один",
|
||||
"RTSP Transport": "Транспорт RTSP",
|
||||
"Range or Single": "Диапазон или одиночный",
|
||||
"Rate": "Скорость <small>(кадров в секунду)</small>",
|
||||
"Record": "Запись",
|
||||
"Record File Type": "Тип Записи Файла",
|
||||
"Record Height": "Рекордную Высоту",
|
||||
"Record Video Filter": "Запись Видео Фильтр",
|
||||
"Record Width": "Запись Ширина",
|
||||
"Record File Type": "Тип файла записи",
|
||||
"Record Height": "Высота записи",
|
||||
"Record Video Filter": "Видео-фильтр записи",
|
||||
"Record Width": "Ширина записи",
|
||||
"Recording": "Запись",
|
||||
"Recording Flags": "Запись Флаги",
|
||||
"Recording Segment Interval": "Запись сегмента интервала <small>в минутах</small>",
|
||||
"Recording Timeout": "Запись тайм-аута <small>в минутах</small>",
|
||||
"Recording Timestamp": "Метки Записи",
|
||||
"Recording Watermark": "Запись Водяной Знак",
|
||||
"RecordingText": "Рекомендуется установить <b>рекорд типа файла</b> в <b class=\"h_t_input h_t_jpeg h_t_socket\">формате WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">и MP4</b> и <b>видео кодеков</b> для <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">копия или </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">поддержкой libx264</b> потому что твой <b>ввод Тип</b> имеет значение <b class=\"h_t_text\"></b>.",
|
||||
"Recording Flags": "Флаги записи",
|
||||
"Recording Segment Interval": "Интервал сегмента записи <small>в минутах</small>",
|
||||
"Recording Timeout": "Тайм-аут записи <small>в минутах</small>",
|
||||
"Recording Timestamp": "Временная метка записи",
|
||||
"Recording Watermark": "Водяной знак на записи",
|
||||
"RecordingText": "Рекоменжуется установить <b>Тип файла записи</b> в <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">MP4</b> и <b>Видео-кодек</b> в <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copy or </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b>, потому что <b>Тип ввода</b> установлен в <b class=\"h_t_text\"></b>.",
|
||||
"Refresh List of Cascades": "Обновить список каскадов",
|
||||
"Region Editor": "Редактор Области",
|
||||
"Region Name": "Название Области",
|
||||
"RegionNote": "Точки сохраняются только при нажатии <b>Сохранить</b> на <b>монитор настройки</b> окна.",
|
||||
"Regions": "Регионы",
|
||||
"Remember Me": "Запомнить Меня",
|
||||
"Reset Timer": "Сброс Таймера",
|
||||
"Restarting Process": "Процесс Перезагрузки",
|
||||
"Retry Connection": "Повторить попытку подключения <small>несколько раз допустить провала</small>",
|
||||
"Retrying...": "Повторение...",
|
||||
"Right": "Правильный <small>URL-Адрес</small>",
|
||||
"Right Stop": "Право прекратить <small>URL-Адрес</small>",
|
||||
"Region Editor": "Редактор областей",
|
||||
"Region Name": "Название области",
|
||||
"RegionNote": "Точки сохраняются только при нажатии <b>Сохранить</b> в окне <b>настройки монитора</b>.",
|
||||
"Regions": "Области",
|
||||
"Remember Me": "Запомнить меня",
|
||||
"Reset Timer": "Сброс таймера",
|
||||
"Restarting Process": "Процесс перезагрузки",
|
||||
"Retry Connection": "Количество попыток подключения <small>допустимое количество неудачных попыток</small>",
|
||||
"Retrying...": "Повтор...",
|
||||
"Right": "Вправо <small>URL</small>",
|
||||
"Right Stop": "Остановить вправо <small>URL</small>",
|
||||
"Rotate": "Повернуть",
|
||||
"Save": "Сохранить",
|
||||
"Save Directory": "Сохранить Каталог",
|
||||
"Save Events to SQL": "Сохранять события в SQL",
|
||||
"Save Log in SQL": "Сохранить журнал в SQL <small>это может быстро заполняются.</small>",
|
||||
"Save Directory": "Каталог для сохранения",
|
||||
"Save Events to SQL": "Сохранять события в БД",
|
||||
"Save Log in SQL": "Сохранить журнал в БД <small>Следите щза размером БД, т.к. она может быстро заполниться</small>",
|
||||
"Save as": "Сохранить как",
|
||||
"Saved Filters": "Сохраненные Фильтры",
|
||||
"Scan Settings": "Параметры Сканирования",
|
||||
"Saved Filters": "Сохраненные фильтры",
|
||||
"Scan Settings": "Параметры сканирования",
|
||||
"Search": "Поиск",
|
||||
"Send Frames": "Отправить кадры <small>пуш-периодах для анализа</small>",
|
||||
"Send Frames": "Отправить кадры <small>для анализа</small>",
|
||||
"Separate with commas, no spaces": "Отделить запятыми, без пробелов",
|
||||
"Set to Watch Only": "Установить, чтобы смотреть только",
|
||||
"Settings": "Параметры",
|
||||
"Settings Changed": "Настройки Изменились",
|
||||
"Set to Watch Only": "Установить только для просмотра",
|
||||
"Settings": "Настройки",
|
||||
"Settings Changed": "Настройки изменены",
|
||||
"SettingsChangedText": "Ваши настройки были сохранены и применены.",
|
||||
"Shinobi": "Шиноби",
|
||||
"Shinobi Streamer": "Шиноби Серпантин",
|
||||
"Show Logs": "Показать Журналы",
|
||||
"Silent": "Молчит",
|
||||
"Shinobi": "Shinobi",
|
||||
"Shinobi Streamer": "Трансляция Shinobi Streamer",
|
||||
"Show Logs": "Показать журнал",
|
||||
"Silent": "Молча",
|
||||
"Simple": "Простой",
|
||||
"Size (mb)": "Размер (Мб)",
|
||||
"Snapshot": "Снимок",
|
||||
"Snapshot Flags": "Снимок Флаги",
|
||||
"Snapshot Flags": "Флаги снимка",
|
||||
"Snapshots": "Снимки",
|
||||
"Sort By": "Сортировать По",
|
||||
"Start": "Начать",
|
||||
"Start Recording": "Начать Запись",
|
||||
"Start Time": "Время Начала",
|
||||
"Started": "Начал",
|
||||
"Status Indicator": "Индикатор Состояния",
|
||||
"Stop URL": "Остановить URL",
|
||||
"Stream": "Поток",
|
||||
"Stream Flags": "Флаги Трансляция",
|
||||
"Stream Timestamp": "Метка Поток",
|
||||
"Stream Type": "Тип Потока",
|
||||
"Stream Watermark": "Водяной Поток",
|
||||
"Stream to YouTube": "Трансляцию на YouTube",
|
||||
"Stream to YouTube Flags": "Поток флаги на YouTube",
|
||||
"StreamText": "<p>В этом разделе будет указать основной поток метода и его параметров. Этот поток будет отображаться на приборной панели. Если вы решите использовать ЗОЖ, в формате JPEG или MJPEG, то вы можете потреблять поток через другие программы.</p><p class=\"h_st_input h_st_jpeg\">В формате JPEG потока, по сути, выключает основной поток и использует моментальный снимок ящика, чтобы получить кадры.</p>",
|
||||
"Streamer": "Серпантин",
|
||||
"Sort By": "Сортировать по",
|
||||
"Start": "Начало",
|
||||
"Start Recording": "Начало записи",
|
||||
"Start Time": "Время начала",
|
||||
"Started": "Начато",
|
||||
"Status Indicator": "Индикатор состояния",
|
||||
"Stop URL": "URL остановки",
|
||||
"Stream": "Трансляция",
|
||||
"Stream Flags": "Флаги трансляции",
|
||||
"Stream Timestamp": "Временная метка трансляции",
|
||||
"Stream Type": "Тип потока",
|
||||
"Stream Watermark": "Водяной знак трансляции",
|
||||
"Stream to YouTube": "Транслировать на YouTube",
|
||||
"Stream to YouTube Flags": "Флаги трансляции на YouTube",
|
||||
"StreamText": "<p>Этот раздел описывает основной метод вещания и его настройки. Трансляция будет отображена на приборной доске. Если вы выберете HLS, JPEG, или MJPEG, то вы сможете просматривать поток в других программах.</p><p class=\"h_st_input h_st_jpeg\">Использование JPEG-потока по сути выключит основную трансляцию и приведёт к использованию корзины снимков для получения кадров.</p>",
|
||||
"Streamer": "Вещатель",
|
||||
"Streams": "Потоки",
|
||||
"Superuser": "Суперпользователя",
|
||||
"Switch on for Still Image": "Переключиться на еще изображения",
|
||||
"Superuser": "Суперпользователь",
|
||||
"Switch on for Still Image": "Переключиться на неподвижное изображение",
|
||||
"TCP": "Протокол TCP",
|
||||
"Text Box Color": "Цвет Текстового Поля",
|
||||
"Text Color": "Цвет Текста",
|
||||
"Time-lapse": "Промежуток времени",
|
||||
"Time-lapse Tool": "Инструмент покадровой",
|
||||
"Timeout": "Ожидания",
|
||||
"Timeout Reset on Next Motion": "Таймаут сброса на следующем движении",
|
||||
"Toggle Sidebar": "Переключение Боковой Панели",
|
||||
"Text Box Color": "Цвет текстового поля",
|
||||
"Text Color": "Цвет текста",
|
||||
"Time-lapse": "Интервальная съёмка",
|
||||
"Time-lapse Tool": "Инструменты интервальной съёмки",
|
||||
"Timeout": "Таймаут",
|
||||
"Timeout Reset on Next Motion": "Сброс таймаута при следующем движении",
|
||||
"Toggle Sidebar": "Переключение боковой панели",
|
||||
"Top Left": "Верхний Левый",
|
||||
"Top Right": "Вверху Справа",
|
||||
"Trigger Record": "Триггер Записи",
|
||||
"Trigger Successful": "Триггер Успешным",
|
||||
"UDP": "УДП",
|
||||
"Top Right": "Верхний Правый",
|
||||
"Trigger Record": "Триггер записи",
|
||||
"Trigger Successful": "Триггер успешен",
|
||||
"UDP": "Протокол UDP",
|
||||
"URL": "URL-адрес",
|
||||
"URL Stop Timeout": "Адрес останавливать время ожидания <small>запуска остановки URL после x миллисекунд</small>",
|
||||
"US": "Нам",
|
||||
"URL Stop Timeout": "Таймаут URL остановки <small>вызвать URL после X миллисекунд</small>",
|
||||
"US": "США",
|
||||
"Unable to Launch": "Не удалось запустить",
|
||||
"UnabletoLaunchText": "Пожалуйста, сохраните сначала новый монитор. Затем попытаться запустить редактор регионе.",
|
||||
"Up": "До <small>URL-Адрес</small>",
|
||||
"Up Stop": "До остановки <small>URL-Адрес</small>",
|
||||
"Username": "Имя пользователя",
|
||||
"UnabletoLaunchText": "Пожалуйста, сначала сохраните монитор а затем используйте редактор областей.",
|
||||
"Up": "Вверх <small>URL</small>",
|
||||
"Up Stop": "Остановить вверх <small>URL</small>",
|
||||
"Username": "Логин",
|
||||
"Value": "Значение",
|
||||
"Video": "Видео",
|
||||
"Video Codec": "Видеокодек",
|
||||
"Video Filter": "Видеофильтра",
|
||||
"Video Finished": "Видео Закончил",
|
||||
"Video Length (minutes) and Motion Count per video": "Продолжительность видео (минут) и движения на видео",
|
||||
"Video Record Rate": "Видео Скорость записи <small>(кадров в секунду)</small>",
|
||||
"Video Status": "Статус Видео",
|
||||
"Video and Time Span (Minutes)": "Прямая и отрезок времени (минут)",
|
||||
"Video Codec": "Видео-кодек",
|
||||
"Video Filter": "Видео-фильтр",
|
||||
"Video Finished": "Видео окончено",
|
||||
"Video Length (minutes) and Motion Count per video": "Продолжительность видео (минут) и кол-во движений на видео",
|
||||
"Video Record Rate": "Скорость видео <small>(кадров в секунду)</small>",
|
||||
"Video Status": "Статус видео",
|
||||
"Video and Time Span (Minutes)": "Видео и отрезок времени (минут)",
|
||||
"Videos": "Видео",
|
||||
"Videos List": "Видео Список",
|
||||
"Videos List": "Список видео",
|
||||
"Watch": "Смотреть",
|
||||
"Watch Only": "Смотреть Только",
|
||||
"Watch Only": "Только просмотр",
|
||||
"WebDAV": "Протокол WebDAV",
|
||||
"WebM (libvpx)": "Колсоном (libvpx)",
|
||||
"Webdav Error": "Ошибка В WebDAV",
|
||||
"WebdavErrorText": "Не удается сохранить. Вы сделали камеры папки внутри выбранной сохранить каталог?",
|
||||
"Webhook": "Веб-перехватчик",
|
||||
"Webhook URL": "URL-адрес веб-перехватчик",
|
||||
"WebM (libvpx)": "WebM (libvpx)",
|
||||
"Webdav Error": "Ошибка WebDAV",
|
||||
"WebdavErrorText": "Не удается сохранить. Попробуйте сначала создать каталог.",
|
||||
"Webhook": "Вебхук",
|
||||
"Webhook URL": "URL вебхука",
|
||||
"Width": "Ширина",
|
||||
"Yes": "Да",
|
||||
"Zoom In": "Зум в <small>URL-Адрес</small>",
|
||||
"Zoom In Stop": "Зум в <small>URL-Адрес</small>",
|
||||
"Zoom Out": "Уменьшать <small>URL-Адрес</small>",
|
||||
"Zoom Out Stop": "Уменьшать остановка <small>URL-Адрес</small>",
|
||||
"a day": "в день",
|
||||
"Zoom In": "Увеличение <small>URL</small>",
|
||||
"Zoom In Stop": "Остановка увеличение<small>URL</small>",
|
||||
"Zoom Out": "Уменьшить <small>URL</small>",
|
||||
"Zoom Out Stop": "Остановка уменьшения <small>URL-Адрес</small>",
|
||||
"a day": "день",
|
||||
"a few seconds": "несколько секунд",
|
||||
"a minute": "минуту",
|
||||
"a month": "месяц",
|
||||
|
@ -453,44 +453,45 @@
|
|||
"ac3": "АС3",
|
||||
"ago": "назад",
|
||||
"an hour": "час",
|
||||
"blankPassword": "Оставьте поле пустым, чтобы сохранить тот же пароль",
|
||||
"blankPassword": "Оставьте поле пустым, чтобы сохранить текущий пароль",
|
||||
"calendar": "календарь",
|
||||
"clientStreamFailedattemptingReconnect": "На стороне клиента проверить ctream не удалось, попытка воссоединиться.",
|
||||
"confirmDeleteFilter": "Вы действительно хотите удалить этот фильтр? Нельзя восстановить.",
|
||||
"clientStreamFailedattemptingReconnect": "Проверка потока на стороне клиента провалилась, попытка переподключения",
|
||||
"confirmDeleteFilter": "Вы действительно хотите удалить этот фильтр? Это действие нельзя отменить.",
|
||||
"copy": "копия",
|
||||
"days": "дней",
|
||||
"dropBoxSuccess": "Успехов! Файлы, сохраненные в Dropbox.",
|
||||
"dropBoxSuccess": "Успех! Файлы сохранены в Dropbox.",
|
||||
"for Global Access": "для глобального доступа",
|
||||
"hours": "часов",
|
||||
"in": "в",
|
||||
"libmp3lame": "libmp3lame",
|
||||
"libopus": "libopus",
|
||||
"libvorbis (Default)": "libvorbis были (по умолчанию)",
|
||||
"libvorbis (Default)": "libvorbis (по умолчанию)",
|
||||
"libvpx (Default)": "libvpx (по умолчанию)",
|
||||
"libvpx-vp9": "libvpx-vp9 или",
|
||||
"libx264": "поддержкой libx264",
|
||||
"libx264 (Default)": "поддержкой libx264 (по умолчанию)",
|
||||
"libvpx-vp9": "libvpx-vp9",
|
||||
"libx264": "libx264",
|
||||
"libx264 (Default)": "libx264 (по умолчанию)",
|
||||
"libx265": "libx265",
|
||||
"minutes": "минут",
|
||||
"modifyVideoText1": "Метод не существует. Убедитесь, что последнее значение URL является не пустым.",
|
||||
"monitorEditFailedMaxReached": "Ваша учетная запись, достигнуто Максимальное число камер, которые могут быть созданы. Поговорить с администратором, если вы хотели бы это изменить.",
|
||||
"monitorEditText1": "Неверные данные, проверьте, чтобы увидеть это допустимая строка импорт.",
|
||||
"monitorEditText2": "Поврежденных Деталей Строку. Проверьте, это json-строку, а не обычный объект был принят.",
|
||||
"monitorGetText1": "неполный запрос, удалить последний слеш в URL или положить приемлемой стоимости.",
|
||||
"modifyVideoText1": "Метод не существует. Убкдитесь, что последнее значение URL не пустое.",
|
||||
"monitorEditFailedMaxReached": "Достигнт предел создаваемых камер для вашего аккаунта. Свяжитесь с вашим администратором для изменения предела.",
|
||||
"monitorEditText1": "Неверные данные - убедитесь, что введена корректная строка для импорта",
|
||||
"monitorEditText2": "Неверная строка деталей. Убедитесь, что введа корректная JSON-строка вместо обычного объекта.",
|
||||
"monitorGetText1": "неполный запрос, уберите слэш в конце URL или введите приемлимое значение.",
|
||||
"months": "месяцев",
|
||||
"noSpecialCharacters": "Без пробелов или специальных символов.",
|
||||
"on": "на",
|
||||
"on Error": "в случае ошибки",
|
||||
"startUpText0": "проверка размера для видео",
|
||||
"startUpText1": "конец проверить Размер для видео",
|
||||
"startUpText2": "все пользователи проверены, подождите закрывать открытые файлы и удалить файлы через лимит пользователей",
|
||||
"startUpText3": "ждал недостроенный видео проверяем какое-то время. 3 секунды.",
|
||||
"startUpText4": "начиная все мониторы установить для просмотра и записи",
|
||||
"startUpText5": "Шиноби готов.",
|
||||
"superAdminText": "\"супер.в json\" не существует. Пожалуйста, переименуйте \"супер.образец.в json\" на \"супер.в json\".",
|
||||
"superAdminTitle": "Шиноби : Супер Админ",
|
||||
"total": "общая",
|
||||
"updateKeyText1": "\"updateKey\" отсутствует \"конф.с json\", нельзя делать обновление таким образом, пока вы не добавите его.",
|
||||
"updateKeyText2": "\"updateKey\" - это неправильно.",
|
||||
"startUpText0": "Проверка используемого диска...",
|
||||
"startUpText1": "Проверка используемого диска окончена.",
|
||||
"startUpText2": "все пользователи проверены, ожидаю закрытия открытых файлов и удаления файлов, превышающих ограничения пользователей",
|
||||
"startUpText3": "даю немного времени на завершение проверки видео. 3 секунды.",
|
||||
"startUpText4": "Запуск мониторов... Пожалуйста, ожидайте...",
|
||||
"startUpText5": "Shinobi готов.",
|
||||
"startUpText6": "Несвязанные видео найдены и добавлены",
|
||||
"superAdminText": "Файл \"super.json\" не существует. Переименуйте \"super.sample.json\" в \"super.json\".",
|
||||
"superAdminTitle": "Shinobi: Супер Админ",
|
||||
"total": "итого",
|
||||
"updateKeyText1": "\"updateKey\" отсутствует в \"conf.json\", невозможно провести обновление, пока он не будет добавлен.",
|
||||
"updateKeyText2": "\"updateKey\" неверный.",
|
||||
"years": "лет"
|
||||
}
|
||||
|
|
966
languages/sv_SE.json
Normal file
966
languages/sv_SE.json
Normal file
|
@ -0,0 +1,966 @@
|
|||
{
|
||||
"# of Allow MJPEG Clients": "# av tillåtna MJPEG Klienter <small>0 för oändlig</small>",
|
||||
"180 Degrees": "180 Grader",
|
||||
"2-Factor Authentication": "2-Faktors Autentisering",
|
||||
"90 Clockwise and Vertical Flip": "90 Medurs och Vertikal Flip",
|
||||
"90 Clockwise": "90 Medurs",
|
||||
"90 Counter Clockwise and Vertical Flip (default)": "90 Moturs och Vertikal Flip (standard)",
|
||||
"AND": "OCH",
|
||||
"API Key Added": "API-Nyckel tillagd",
|
||||
"API Key Deleted": "API-Nyckel Borttagen",
|
||||
"API Key": "API-Nyckel",
|
||||
"API Keys": "API-Nycklar",
|
||||
"API": "API",
|
||||
"APIKeyAddedText": "Du kan använda den här API Nyckeln nu.",
|
||||
"APIKeyDeletedText": "API Nyckeln har tagits bort. Den kommer inte längre att fungera.",
|
||||
"Enter atleast one IP": "Ange minst ett IP",
|
||||
"DeleteAPIKeyMsg": "Vill du radera denna API Nyckel? Du kan inte återskapa den.",
|
||||
"ASC": "Stigande",
|
||||
"Accelerator": "Accelerator",
|
||||
"Account Info": "Konto Information",
|
||||
"AccountEditText1": "Kunde inte redigera. Uppdatera sidan om problemet fortsätter.",
|
||||
"Accounts": "Konton",
|
||||
"Action for Selected": "Åtgärder för Utvalda",
|
||||
"Add Channel": "Lägg Till Kanal",
|
||||
"Add Input Feed": "Lägg till Inmatningsflöde",
|
||||
"Add Map": "Lägg Till Karta",
|
||||
"Add Monitor": "Lägg Till Kamera",
|
||||
"Add New": "Lägg Till Ny",
|
||||
"Add": "Lägg till",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avancerad",
|
||||
"Again": "Igen",
|
||||
"Age": "Ålder",
|
||||
"Alert Sound Delay": "Signal Fördröjning",
|
||||
"Alert Sound": "Varning Ljud",
|
||||
"All Logs": "Alla Loggar",
|
||||
"All Monitors and Privileges": "Alla Kameror och Privilegier",
|
||||
"All Monitors": "Alla Kameror",
|
||||
"All Privileges": "Alla Privilegier",
|
||||
"All Warnings": "Alla Varningar",
|
||||
"All streams in first feed": "Alla streams i första flödet",
|
||||
"Allow API Trigger": "Tillåt API Trigger",
|
||||
"Allow Next Command": "Ge Nästa Kommando <small>i Minuter</small>",
|
||||
"Allow Next Discord Alert": "Låt Nästa Discord Alert <small>i Minuter</small>",
|
||||
"Allow Next Email": "Tillåter Nästa E-post <small>inom några Minuter</small>",
|
||||
"Allow Next Trigger": "Tillåt Nästa Trigger <small>i Millisekunder</small>",
|
||||
"Allowed IPs": "Tillåtna IPs",
|
||||
"Already exists": "Finns redan",
|
||||
"Always": "Alltid",
|
||||
"Amazon S3 Upload Error": "Amazon S3 Uppladdnings Fel",
|
||||
"Amazon S3": "Amazon S3",
|
||||
"Analyzation Duration": "Analyzation Längd",
|
||||
"April": "April",
|
||||
"Archive": "Arkiv",
|
||||
"Are you sure?": "Är du säker?",
|
||||
"Attach Video Clip": "Bifoga Video Klipp",
|
||||
"Audio Bit Rate": "Audio Bithastighet",
|
||||
"Audio Codec": "Ljud-Codec",
|
||||
"Audio Detection": "Ljuddetektion",
|
||||
"Audio Detector": "Ljud Detektor",
|
||||
"Audio stream only from first feed": "Ljud endast från första flödet",
|
||||
"Audio streams only": "Ljud streams endast",
|
||||
"Audio": "Ljud",
|
||||
"August": "Augusti",
|
||||
"Authenticate": "Verifiera",
|
||||
"Authentication Failed": "Autentisering Misslyckades",
|
||||
"Auto": "Auto",
|
||||
"Automatic": "Automatisk",
|
||||
"Autosave": "Autospara",
|
||||
"Backblaze B2": "Backblaze B2",
|
||||
"Backblaze Error": "Backblaze Fel",
|
||||
"Base64 over Websocket": "Base64 över Websocket",
|
||||
"Basic Authentication": "Grundläggande Autentisering",
|
||||
"Batch": "Batch",
|
||||
"Bind Credentials": "Bind Referenser (Lösenord)",
|
||||
"Blank for No Change": "Tomt för Ingen Förändring",
|
||||
"Bottom Left": "Längst Ned Till Vänster",
|
||||
"Bottom Right": "Längst Ner Till Höger",
|
||||
"Browser Console Log": "Visa Webläsarens Konsol logg",
|
||||
"Bucket": "Bucket",
|
||||
"Buffer Preview": "Buffert Förhandsgranska",
|
||||
"Build": "Bygga",
|
||||
"Building": "Byggnad",
|
||||
"CPU indicator will not work. Continuing...": "CPU-indikatorn kommer inte att fungera. Fortsätter...",
|
||||
"CPU used by this stream": "CPU som används av denna stream",
|
||||
"CPU": "CPU",
|
||||
"CSS": "CSS <small>format på instrumentpanel.</small>",
|
||||
"Calendar": "Kalendern",
|
||||
"Call Method": "Samtal Metod",
|
||||
"Camera Password": "Kamera Lösenord",
|
||||
"Camera Username": "Kamera Användarnamn",
|
||||
"Camera is not recording": "Kameran inspelar inte in",
|
||||
"Camera is not running": "Kameran körs ej",
|
||||
"Camera is not streaming": "Kameran streamar inte",
|
||||
"CameraNotRecordingText": "Inställningar kan vara inkompatibla. Kolla kodare. Startar om...",
|
||||
"Can Authenticate Websocket": "Kan Autentisera Websocket",
|
||||
"Can Change User Settings": "Kan Ändra Användarinställningar",
|
||||
"Can Control Monitors": "Kan Styra Kameror",
|
||||
"Can Create and Delete Monitors": "Kan Skapa och ta Bort Kameror",
|
||||
"Can Delete Videos and Events": "Kan Radera Videoklipp och Händelser",
|
||||
"Can Delete Videos": "Kan Ta Bort Videor",
|
||||
"Can Edit Monitor": "Kan Redigera Kameror",
|
||||
"Can Get Logs": "Kan Få Loggar",
|
||||
"Can Get Monitors": "Kan Få Kameror",
|
||||
"Can View Logs": "Kan Visa Loggar",
|
||||
"Can View Monitor": "Kan Visa Kamera",
|
||||
"Can View Snapshots": "Kan Visa Bilder",
|
||||
"Can View Streams": "Kan Visa Streams",
|
||||
"Can View Videos and Events": "Kan Visa Videoklipp och Händelser",
|
||||
"Can View Videos": "Kan Visa Videor",
|
||||
"Can edit Max Days": "Kan redigera Max Dagar",
|
||||
"Can edit Max Storage": "Kan redigera Max Lagring",
|
||||
"Can edit how long to keep Events": "Kan ändra hur länge Händelser sparas",
|
||||
"Can edit how long to keep Logs": "Kan ändra hur länge Loggar sparas",
|
||||
"Can use Admin Panel": "Kan använda Admin Panel",
|
||||
"Can use Amazon S3": "Kan använda Amazon S3",
|
||||
"Can use Discord Bot": "Kan använda Discord Bot",
|
||||
"Can use LDAP": "Kan använda LDAP",
|
||||
"Can use SFTP": "Kan använda SFTP",
|
||||
"Can use Wasabi Hot Cloud Storage": "Kan använda Wasabi Hot Cloud Storage",
|
||||
"Can use WebDAV": "Kan använda WebDAV",
|
||||
"Can't Connect": "Kan inte Ansluta",
|
||||
"Cannot watch a monitor that isn't running.": "Kan inte se denna kameran",
|
||||
"Cards": "Kort",
|
||||
"Center": "Center <small>URL-Adress</small>",
|
||||
"Channel ID": "Kanal-ID",
|
||||
"Channel": "Kanal",
|
||||
"Chat on Discord": "Chatta på Discord",
|
||||
"Check Signal Interval": "Kontrollera Signal Intervall <small>i Minuter</small>",
|
||||
"Check for Motion First": "Kontrollera för Motion Först",
|
||||
"Check": "Kontrollera",
|
||||
"Clear Recorder Process": "Rensa Inspelningsprocessen",
|
||||
"Close": "Stäng",
|
||||
"Closed": "Stängdes",
|
||||
"Color Threshold": "Färg Tröskel",
|
||||
"Command on Trigger": "Kommando på Trigger",
|
||||
"Command": "Kommandot",
|
||||
"Complete Stream URL": "Komplett Stream URL",
|
||||
"Conditions": "Villkor",
|
||||
"Confidence of Detection": "Förtroende för Detektering",
|
||||
"Confidence": "Förtroende",
|
||||
"Configuration": "Konfiguration",
|
||||
"Confirm": "Bekräfta",
|
||||
"Connected": "Ansluten",
|
||||
"Connection Type": "Anslutningstyp",
|
||||
"Connection timed out": "Anslutningen har avbrutits",
|
||||
"Connection": "Anslutning",
|
||||
"Contains": "Innehåller",
|
||||
"Control Error": "Kontroll Fel",
|
||||
"Control": "Kontroll",
|
||||
"ControlErrorText1": "Kontroller är inte aktiverat",
|
||||
"ControlErrorText2": "Kontrollera dina anslutnings detaljer. Du kan behöva peka Bas-URL på port 8000 eller 80. Kontrollera din autentisering info.",
|
||||
"Controllable": "Reglerbar",
|
||||
"Controls and Logs": "Kontroller och Loggar",
|
||||
"Copy Connection Settings": "Kopiera Anslutning Inställningar",
|
||||
"Copy Custom Settings": "Kopiera Anpassade Inställningar",
|
||||
"Copy Detector Settings": "Kopiera Detektorinställningar",
|
||||
"Copy Group Settings": "Kopiera Gruppinställningar",
|
||||
"Copy Input Settings": "Kopiera inmatningsinställningar",
|
||||
"Copy JPEG API Settings": "Kopiera JPEG-API Inställningar",
|
||||
"Copy Logging Settings": "Kopiera Inställningar För Loggning",
|
||||
"Copy Mode": "Kopierings Läge",
|
||||
"Copy Recording Settings": "Kopiera Inspelningsinställningar",
|
||||
"Copy Settings": "Kopiera Inställningar",
|
||||
"Copy Stream Channel Settings": "Kopiera Stream Kanal",
|
||||
"Copy Stream Settings": "Kopiera Streaminställningar",
|
||||
"Copy to Settings": "Kopiera till Inställningar",
|
||||
"Could not create Bucket.": "Kunde inte skapa Bucket.",
|
||||
"Country of Plates": "Country of Plates",
|
||||
"Counts of Motion": "Antal Rörelser",
|
||||
"Create Sub-Accounts at /admin": "Skapa Sub-Konton på /admin",
|
||||
"Creating New Account": "Skapa Nytt Konto",
|
||||
"Creation Interval": "Skapa Intervall",
|
||||
"Current": "Nuvarande",
|
||||
"Currently viewing": "Nuvarande tittare ",
|
||||
"Custom Base URL": "Anpassad Bas-URL <small>Lämna tomt för att använda Värd URL</small>",
|
||||
"Custom Endpoint": "Anpassad Slutpunkt",
|
||||
"Custom": "Anpassad",
|
||||
"DB Lost.. Retrying..": "Databasen Förlorad.. Försöker Igen..",
|
||||
"DESC": "Fallande",
|
||||
"Dashboard Language": "Språk Instrumentpanel",
|
||||
"Dashboard": "Instrumentpanel",
|
||||
"Dashcam (Streamer v2)": "Dashcam (Streamer v2)",
|
||||
"Dashcam": "Dashcam",
|
||||
"Database Not Found": "Databasen Hittades Inte",
|
||||
"Database row does not exist": "Databas raden inte finns",
|
||||
"Database": "Databas",
|
||||
"Date Range": "Datum Intervall",
|
||||
"Date": "Datum",
|
||||
"Days": "Dagar",
|
||||
"Debug": "Debug",
|
||||
"December": "December",
|
||||
"Default": "Standard",
|
||||
"Delete Filter": "Ta bort Filter",
|
||||
"Delete Matches": "Ta bort Matchningar",
|
||||
"Delete Monitor State?": "Ta Bort Monitor State",
|
||||
"Delete Monitor States Preset": "Ta Bort Monitor States Preset",
|
||||
"Delete Monitors and Files": "Ta Bort Kameror och Filer",
|
||||
"Delete Monitor": "Ta Bort Kamera",
|
||||
"Delete Motionless Video": "Ta Bort Orörlig Video",
|
||||
"Delete Motionless Videos (Record)": "Ta Bort Orörlig Videor (Inspelad)",
|
||||
"Delete Selected Videos": "Ta Bort Valda Videor",
|
||||
"Delete Video": "Ta Bort Video",
|
||||
"Delete selected": "Radera valda",
|
||||
"Delete": "Ta bort",
|
||||
"DeleteMonitorText": "Vill du ta bort denna kamera? Du kan inte återställa den. Du kan välja att behålla filerna i filsystemet. Om du väljer att återskapa en kamera med samma ID kommer videor och händelser bli synliga i översikten.",
|
||||
"DeleteMonitorsText": "Vill du ta bort dessa Kameror? Du kan inte återställa dem. Du kan välja att behålla filerna för dessa Id:n i filsystemet. Om du väljer att återskapa en kamera med en av de ID:n kommer videor och händelser bli synliga i översikten.",
|
||||
"DeleteSelectedVideosMsg": "Vill du ta bort dessa filmer? Du kan inte återställa dem.",
|
||||
"NoSelectedMsg": "Du måste välja minst en video.",
|
||||
"DeleteVideoMsg": "Vill du ta bort denna video? Du kan inte återställa den.",
|
||||
"Deleted Schedule Configuration": "Borttagen Schema Konfiguration",
|
||||
"Deleted State Configuration": "Borttaget Konfiguration",
|
||||
"Deleted": "Borttagen",
|
||||
"Detect Objects": "Identifiera Objekt <small class=\"\">Se nedan</small>",
|
||||
"Detection Engine": "Detektor Motor",
|
||||
"Detector Buffer": "Detektor Buffert",
|
||||
"Detector Filters": "Detektor Filter",
|
||||
"Detector Flags": "Detektor Flaggor",
|
||||
"Detector Grouping": "Detektor Gruppering <small>Lägga till grupper i <b> Inställningar</b></small>",
|
||||
"Detector Rate": "Detektor Hastighet <small>(FPS)</small>",
|
||||
"Detector Recording Complete": "Detektor Inspelning Komplett",
|
||||
"Detector Recording Process Exited Prematurely. Restarting.": "Detektor Inspelnings Processen Avslutades I Förtid. Startar om.",
|
||||
"Detector": "Detektor",
|
||||
"DetectorText": "<p>När rutorna Bredd och Höjd visas bör du ställa dem till 640 x 480 eller lägre. Detta kommer att optimera läshastigheten av ramar.</p>",
|
||||
"Died": "Dog",
|
||||
"Digest Authentication": "Digest-Autentisering",
|
||||
"Disable Night Vision": "Inaktivera Night Vision <small>URL-Adress</small>",
|
||||
"Disable Nightvision": "Inaktivera Nightvision",
|
||||
"Disabled": "Inaktiverad",
|
||||
"Discord Alert on Trigger": "Discord Alert med Trigger",
|
||||
"Discord Bot": "Discord Bot",
|
||||
"Discord": "Discord",
|
||||
"DiscordErrorText": "Skicka till Disharmoni har orsakat ett Fel",
|
||||
"DiscordFailedText": "Skicka till Discord har Misslyckats",
|
||||
"DiscordLoggedIn": "Discord Bot Autentiserad",
|
||||
"DiscordNotEnabledText": "Discord Bot är Inte Aktiverad, Aktivera det i dina kontoinställningar.",
|
||||
"Documentation": "Dokumentation",
|
||||
"Does Not Contain": "Innehåller Inte",
|
||||
"Don't show this anymore": "Visa inte det här längre",
|
||||
"Double Quote Directory": "Dubbel Citat Katalog <small>Vissa kataloger har utrymmen. Kan krascha vissa kameror.</small>",
|
||||
"Down Stop": "Stopp ner <small>URL-Adress</small>",
|
||||
"Down": "Ner <small>URL-Adress</small>",
|
||||
"Download": "Ladda ner",
|
||||
"Downloading Videos": "Laddar ner Videor",
|
||||
"EU": "EU",
|
||||
"Edit Selected": "Redigera Markerad",
|
||||
"Edit": "Ändra",
|
||||
"Edited Schedule Configuration": "Redigerad Schema Konfiguration",
|
||||
"Edited State Configuration": "Redigerat Tillstånd Konfiguration",
|
||||
"Email Details": "E-Post Info",
|
||||
"Email address is in use.": "E-postadress är i bruk.",
|
||||
"Email and Password fields cannot be empty": "E-postadress och Lösenords fält får inte vara tomt",
|
||||
"Email on No Motion": "E-post på \"No Motion\"",
|
||||
"Email on Trigger": "E-post Utlösare <small>E-post gå till kontohavarens loggain adress.</small>",
|
||||
"Email": "E-post",
|
||||
"Emotion Average": "Känslor Genomsnitt",
|
||||
"Emotion": "Känslor",
|
||||
"Enable Night Vision": "Aktivera Night Vision <small>URL-Adress</small>",
|
||||
"Enable Nightvision": "Aktivera Nightvision",
|
||||
"Enable": "Aktivera",
|
||||
"Enabled": "Aktiverad",
|
||||
"End Time": "Slut Tid",
|
||||
"End": "Slut",
|
||||
"Ended": "Slutade",
|
||||
"Endpoint Address": "Slutpunkt Adress",
|
||||
"Endpoint": "Slutpunkt",
|
||||
"Enlarge": "Förstora",
|
||||
"Enter this code to proceed": "Skriv in denna kod för att gå vidare",
|
||||
"Equal to": "Lika med",
|
||||
"Error Connecting": "Fel Vid Anslutning",
|
||||
"Error While Decoding": "Fel Vid Avkodning",
|
||||
"ErrorWhileDecodingText": "Din hårdvara kan ha en instabil anslutning till nätverket. Kontrollera dina nätverksanslutningar.",
|
||||
"Event Limit": "Händelse Gräns",
|
||||
"Event Webhook Error": "Händelse Webhook Fel",
|
||||
"Event": "Händelse",
|
||||
"EventText1": "Utlöst en motion till händelse",
|
||||
"EventText2": "Kunde inte eposta bild, filen var inte tillgänglig",
|
||||
"Events": "Händelser",
|
||||
"Example": "Exempel",
|
||||
"Execute Command": "Utföra Kommandot",
|
||||
"Executed": "Genomförda",
|
||||
"Export Selected Videos": "Exportera Valda Videor",
|
||||
"Export Video": "Exportera Video",
|
||||
"Export": "Exportera",
|
||||
"ExportSelectedVideosMsg": "Vill du exportera dessa filmer? Det kan ta lite tid att packa och ladda ner.",
|
||||
"FFmpegCantStart": "FFmpeg Kunde inte Starta",
|
||||
"FFmpegCantStartText": "Inspelnings motorn för att den här kameran kan inte starta. Det kan vara något fel med din kamera konfiguration. Om det finns loggar om annat än detta, att posta dem i <b>Issues</b> på Github.",
|
||||
"FFmpegTip": "FFprobe är en enkel multimedia streammar analyzer. Du kan använda den för att visa alla typer av information om en ingång även i fråga om tid, bildhastighet, bildstorlek, etc.",
|
||||
"FFprobe": "Probe",
|
||||
"FLV Stream Type": "FLV-Stream-Typ",
|
||||
"FLV": "FLV",
|
||||
"FactorAuthText1": "Koden är endast aktiv i 15 minuter. Om du loggar in igen, kommer timern att återställas till 15 minuter med samma kod.",
|
||||
"Fatal Maximum Reached": "Dödligt Max Nått, Stoppa Kameran.",
|
||||
"Fatal": "Allvarligt",
|
||||
"FatalMaximumReachedText": "Allvarligt JPEG Fel.",
|
||||
"February": "Februari",
|
||||
"Feed-in Image Height": "Feed-in Bildens Höjd",
|
||||
"Feed-in Image Width": "Feed-in Bildens Bredd",
|
||||
"Female": "Kvinna",
|
||||
"Fields cannot be empty": "Fält får inte vara tomt",
|
||||
"File Delete Error": "Fel vid radering av fil",
|
||||
"File Not Exist": "Filen Existerar Inte",
|
||||
"File Not Found in Database": "Filen kan Inte Hittas i Databasen",
|
||||
"File Not Found in Filesystem": "Filen kan Inte Hittas i Filsystemet",
|
||||
"File Not Found": "Filen Kunde Inte Hittas",
|
||||
"File Type": "Filtyp",
|
||||
"FileBin Share": "FileBin Share",
|
||||
"FileNotExistText": "Det går inte att spara icke existerande fil. Något gick fel.",
|
||||
"Filename": "Filnamn",
|
||||
"Filesize": "Filstorlek",
|
||||
"Filter ID": "Filter-ID",
|
||||
"Filter Matches": "Filter Matchar",
|
||||
"Filter Name": "Filtrera Namn",
|
||||
"Filter for Objects only": "Filter för Objekt",
|
||||
"FilterMatchesText1": "Detta filter har uppfyllt villkoren.",
|
||||
"FilterMatchesText2": "videor som finns.",
|
||||
"Filters Updated": "Filter Uppdaterade",
|
||||
"Filters": "Filter",
|
||||
"FiltersUpdatedText": "Dina ändringar har sparats och tillämpas.",
|
||||
"Find Where": "Hitta Där",
|
||||
"First stream in feed": "Första stream i flödet",
|
||||
"Fix Video": "Fixa Video",
|
||||
"Fix": "Fixa",
|
||||
"FixVideoMsg": "Vill du fixa denna video? Du kan inte ångra den här åtgärden.",
|
||||
"Flush PM2 Logs": "Spola PM2 Loggar",
|
||||
"Font Path": "Font Sökväg",
|
||||
"Font Size": "Font Storlek",
|
||||
"For Group": "För Gruppen",
|
||||
"Force Monitors Per Row": "Tvinga Kameror Per Rad",
|
||||
"Force Port": "Tvinga Port",
|
||||
"Form Data Not Found": "Formulär För Uppgifter Som Inte Finns",
|
||||
"Found Devices": "Hittade Enheter",
|
||||
"Frame Rate": "Bildhastighet",
|
||||
"Friday": "Fredag",
|
||||
"Full Frame Detection": "Full Bild Detektion",
|
||||
"Full Stream URL": "Full Stream URL",
|
||||
"Full URL Path": "Fullständiga URL-Sökvägen",
|
||||
"Fullscreen": "Fullskärm",
|
||||
"Gender": "Kön",
|
||||
"Generate Subtitles": "Generera Undertexter",
|
||||
"Get Logs to Client": "Få Loggar till Klienten",
|
||||
"Global Detector Settings": "Globala Detektor Inställningar",
|
||||
"Greater Than or Equal to": "Större Än eller Lika med",
|
||||
"Greater Than": "Större Än",
|
||||
"Group Key is in use.": "Grupp Nyckeln används redan.",
|
||||
"Group Key": "Grupp Nyckel",
|
||||
"Group Name": "Grupp Namn",
|
||||
"Grouping": "Gruppering",
|
||||
"H.264 / H.265 / H.265+": "H.264 / H.265 / H.265 ",
|
||||
"HEVC (H.265)": "HEVC (H. 265)",
|
||||
"HLS (.m3u8)": "HLS (.m3u8)",
|
||||
"HLS (includes Audio)": "HLS (innehåller Ljud)",
|
||||
"HLS Audio Encoder": "Audio Encoder",
|
||||
"HLS List Size": "List Storlek",
|
||||
"HLS Live Start Index": "HLS Live Start Index",
|
||||
"HLS Preset": "Förinställd Mall",
|
||||
"HLS Segment Length": "Segment Längd <small>i Sekunder</small>",
|
||||
"HLS Start Number": "Start Nummer",
|
||||
"HLS Video Encoder": "Video Encoder",
|
||||
"HTTP": "HTTP",
|
||||
"HTTPS": "HTTPS",
|
||||
"Hardware Accelerated": "Hårdvaruaccelererad",
|
||||
"Height": "Höjd",
|
||||
"Help": "Hjälp",
|
||||
"Hide List": "Dölj Lista",
|
||||
"Hide Notes": "Dölj Notiser",
|
||||
"Host Type": "Värd Typ",
|
||||
"Host": "Värd",
|
||||
"Hotswap Modes (Watch-Only)": "Hotswap Lägen (Titta Endast)",
|
||||
"How to Record": "Inspelningsläge",
|
||||
"IP Address": "IP-Adress",
|
||||
"Identity": "Identitet",
|
||||
"IdentityText1": "Detta är hur systemet kommer att identifiera data för denna stream. Du kan inte ändra <b>Kamera ID</b> när du har tryckt på spara. Om du vill kan du göra <b>Kamera ID</b> mer läsbar innan du fortsätter.",
|
||||
"IdentityText2": "Du kan kopiera en kamera genom att ändra <b>Kamera ID</b> och sedan trycka på spara. Du kan <b>inte</b> använda samma ID på en kamera som redan finns eller kommer det att spara över kamerans databas information.",
|
||||
"Idle": "Inaktiv",
|
||||
"Image Height": "Bildens Höjd",
|
||||
"Image Location": "Bild Sökväg <small>Absolut Sökväg eller lämna tomt för att använda globala</small>",
|
||||
"Image Position": "Bild Position",
|
||||
"Image Width": "Bildens Bredd",
|
||||
"Import Monitor Configuration": "Importera Kameror",
|
||||
"Import": "Importera",
|
||||
"ImportMonitorConfigurationText": "Om du gör detta kommer eventuella förändringar som för närvarande inte är sparade att skrivas över. Importerade ändringar kommer att tillämpas endast när du trycker på <b>Spara</b>.",
|
||||
"ImportMultiMonitorConfigurationText": "Om du gör detta, kommer alla Kameror skrivas över med Id:n från importfilen.",
|
||||
"In": "I",
|
||||
"Incorrect Settings Chosen": "Felaktiga Inställningar Valt",
|
||||
"Indifference": "Indifferens",
|
||||
"Input Feed": "Inmatningsflöde",
|
||||
"Input Flags": "Inmatningsflaggor",
|
||||
"Input Selector": "Ingångsväljare",
|
||||
"Input Settings": "Inmatningsinställningar",
|
||||
"Input Type": "Ingångstyp",
|
||||
"Input": "Inmatning",
|
||||
"InputText1": "I detta avsnitt berättar Shinobi hur man tar emot en stream. För optimal prestanda, försök finjustera kamerans interna inställningar. Hitta följande alternativ och ställa in dem som på bilden. För att hitta din kamera kan du använda den <b>inbyggda ONVIF-Scannern</b> av Shinobi. Vissa ONVIF-kameror kräver ett verktyg för att ändra sina interna inställningar. Om du inte kan hitta dina kameror kan du prova <a href=\"http://cdn.shinobi.video/installers/odm-v2.2.250.msi\">ONVIF enhetshanteraren för Windows</a>.",
|
||||
"InputText2": "<ul><li><b>Bildfrekvens (FPS) :</b> Hög : 10 - 15 bildrutor per sekund, low (Låg): 2-5 FPS</li><li><b>I-frame intervall :</b> 1</li><li><b>Bithastighet Typ :</b> Variabel bithastighet (VBR)</li><li><b>Bithastighet :</b> mellan 256kbps - 1000kbps</li></ul>",
|
||||
"InputText3": "Om du behöver hjälp vilken inmatningstyp din kamera har att du kan ta en titt i <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Kameran Webbadresser Lista</a> på Shinobi webbplats.",
|
||||
"Inserted Schedule Configuration": "Infogad Schema Konfiguration",
|
||||
"Inserted State Configuration": "Infogad State Konfiguration",
|
||||
"Invalid Data": "Ogiltig Data",
|
||||
"Invalid JSON": "Ogiltig JSON",
|
||||
"InvalidJSONText": "Se till att det är en giltig JSON-sträng för Shinobi Kameror.",
|
||||
"JPEG (Auto Enables JPEG API)": "JPEG (Auto Aktiverar JPEG-API)",
|
||||
"JPEG API": "JPEG-API",
|
||||
"JPEG Error": "JPEG Fel",
|
||||
"JPEG Mode": "JPEG-Läge",
|
||||
"JPEG": "JPEG",
|
||||
"JPEGErrorText": "Det var ett problem att få data från din kamera.",
|
||||
"January": "Januari",
|
||||
"July": "Juli",
|
||||
"June": "Juni",
|
||||
"LDAP Success": "LDAP Lyckad",
|
||||
"LDAP User Authenticated": "LDAP-Användare Autentiseras",
|
||||
"LDAP User is New": "LDAP-Användare är Ny",
|
||||
"LDAP": "LDAP",
|
||||
"Landing Page": "Målsidan",
|
||||
"Launch in New Window": "Starta i Nytt Fönster",
|
||||
"Leave blank for random.": "Lämna tomt för slumpmässiga.",
|
||||
"Leave blank for unlimited": "Lämna tomt för obegränsad",
|
||||
"Left Stop": "Vänster Stop <small>URL-Adress</small>",
|
||||
"Left": "Vänster <small>URL-Adress</small>",
|
||||
"Less Than or Equal to": "Mindre Än eller Lika med",
|
||||
"Less Than": "Mindre Än",
|
||||
"License Plate Detector": "Registreringsskylts Detektor",
|
||||
"Like": "Som",
|
||||
"Limited": "Begränsad",
|
||||
"Link Shinobi": "Länka Shinobi",
|
||||
"List Toggle": "Växla Lista",
|
||||
"List of Videos Delete Error": "Lista över Videor ta Bort Fel",
|
||||
"Live Stream Toggle": "Live Stream Växla",
|
||||
"Live View": "Live View",
|
||||
"Local": "Lokal",
|
||||
"Log Level": "Logg Nivå",
|
||||
"Log Signal Event": "Logga Signal Händelser <small>Endast Klient</small>",
|
||||
"Log Stream": "Logga stream",
|
||||
"Logging": "Loggning",
|
||||
"Login": "Logga in",
|
||||
"Logout": "Logga ut",
|
||||
"Logged in": "Inloggad",
|
||||
"Logs": "Loggar",
|
||||
"Loop Stream": "Loopa stream",
|
||||
"MB": "MB",
|
||||
"MJPEG": "MJPEG",
|
||||
"MP4 (copy, libx264, libx265)": "MP4 (kopiera, libx264, libx265)",
|
||||
"MPEG-4 (.mp4 / .ts)": "MPEG-4 - (.mp4 / .ts)",
|
||||
"MPEG-DASH (includes Audio)": "MPEG-DASH (innehåller Ljud)",
|
||||
"MailError": "MAIL ERROR : Kan inte skicka e-post, Kontrollera conf.json. Hoppa över alla funktioner som bygger på utskick.",
|
||||
"Main": "Viktigaste",
|
||||
"Male": "Hane",
|
||||
"Manual": "Manuell",
|
||||
"Map": "Karta",
|
||||
"March": "Mars",
|
||||
"Matches": "Matcher",
|
||||
"Matrices": "Matriser",
|
||||
"Max Indifference": "Max Indifferens",
|
||||
"Max Latency": "Max Fördröjning",
|
||||
"Max Number of Cameras": "Max antal Kameror",
|
||||
"Max Storage Amount": "Max Lagring Storlek",
|
||||
"Maximum dB": "Maximal dB",
|
||||
"May": "Maj",
|
||||
"Merge Selected Videos": "Sammanfoga Valda Videor",
|
||||
"Merge Video": "Koppla Video",
|
||||
"Merge and Download": "Sammanfoga och Ladda ner",
|
||||
"MergeSelectedVideosMsg": "Vill du slå samman dessa filmer? Det kan ta lite tid att koppla av och ladda ner. Nu anslutningen är stängd filen kommer att raderas. Se till att du hålla webbläsaren är öppen tills den är klar.",
|
||||
"Migrator": "Migrator",
|
||||
"Minimum dB": "Minsta dB",
|
||||
"Mode": "Läge",
|
||||
"Monday": "Måndag",
|
||||
"Monitor Added by user": "Kamera tillagd av användaren.",
|
||||
"Monitor Capture Rate": "Kamera Hastighet <small>(FPS)</small>",
|
||||
"Monitor Groups": "Kamera Grupper",
|
||||
"Monitor ID": "Kamera ID",
|
||||
"Monitor Idling": "Kamera inaktiveras",
|
||||
"Monitor Name": "Kamera Namn",
|
||||
"Monitor Settings": "Kamera Inställningar",
|
||||
"Monitor States and Schedules": "Monitor States och Scheman",
|
||||
"Monitor States": "Monitor States",
|
||||
"Monitor Stopped": "Kamera Stoppad",
|
||||
"Monitor Updated by user": "Kameran Uppdaterad av användaren.",
|
||||
"Monitor is now Disabled": "Kamera är nu Inaktiverad",
|
||||
"Monitor is now Idle": "Kamera är nu Inaktiv",
|
||||
"Monitor is now Recording": "Kamera är nu på Inspelning",
|
||||
"Monitor is now Watching": "Kameran är nu på Titta på",
|
||||
"Monitor mode changed": "Kamera läget förändrats",
|
||||
"Monitor mode is already": "Kamera läget är redan",
|
||||
"Monitor or Key does not exist.": "Kamera eller Nyckeln inte finns.",
|
||||
"Monitor": "Kamera",
|
||||
"MonitorIdlingText": "Kamera session har blivit inaktiv.",
|
||||
"MonitorStoppedText": "Kamera session har stoppas.",
|
||||
"Monitors per row": "Kameror per rad",
|
||||
"Monitors to Copy to": "Kameror att kopiera till",
|
||||
"Monitors": "Kameror",
|
||||
"Montage": "Montage",
|
||||
"Motion Detection": "Rörelsedetektor",
|
||||
"Motion GUI": "Rörelse GUI",
|
||||
"Motion Meter": "Rörelse Mätare",
|
||||
"Motion": "Rörelse",
|
||||
"Mp4Frag": "Mp4Frag",
|
||||
"Must be atleast one row": "Måste vara minst en rad",
|
||||
"Mute Audio": "Stäng av ljud",
|
||||
"NVIDIA": "NVIDIA",
|
||||
"Name cannot be empty.": "Namn inte kan vara tomt.",
|
||||
"Name": "Namn",
|
||||
"Never": "Aldrig",
|
||||
"New Authentication Token": "Ny Autentiseringstoken",
|
||||
"New Monitor": "Ny Kamera",
|
||||
"Next Video": "Nästa Video",
|
||||
"No Audio": "Inget Ljud",
|
||||
"No Data": "Ingen Data",
|
||||
"No Events found for this video": "Inga händelser hittades för denna video",
|
||||
"No Group with this key exists": "Ingen grupp med denna nyckel finns",
|
||||
"No Monitor Exists with this ID.": "Ingen kamera finns med detta ID.",
|
||||
"No Monitor Found, Ignoring Request": "Ingen kamera finns, Ignorerar begäran",
|
||||
"No Monitor ID Present in Form": "Inget kamera-id finns i formuläret",
|
||||
"No Monitors Selected": "Inga Kameror valda",
|
||||
"No Region": "Ingen Region",
|
||||
"No Rotation": "Ingen Rotation",
|
||||
"No Videos Found": "Inga Videor Finns",
|
||||
"No Videos Selected": "Inga videor valda",
|
||||
"No such file": "Ingen sådan fil",
|
||||
"No": "Nej",
|
||||
"NoLogsFoundForDateRange": "Inga Loggar som finns i detta datumintervall. Försök utöka datumintervallet.",
|
||||
"NoMotionEmailText1": "Ingen Rörelse för",
|
||||
"NoMotionEmailText2": "Det har inte funnits någon rörelse på kameran för",
|
||||
"NoVideosFoundForDateRange": "Inga Videor som finns i detta datumintervall. Prova att ställa in start datumet längre tillbaka.",
|
||||
"Noise Filter Range": "Brusfilterområde",
|
||||
"Noise Filter": "Brusfilter",
|
||||
"Not Authorized": "Inte Auktoriserad",
|
||||
"Not Connected": "Inte Ansluten",
|
||||
"Not Equal to": "Inte Lika med",
|
||||
"Not Found": "Hittades Inte",
|
||||
"Not In": "Inte I",
|
||||
"Not Matches": "Inte Matchar",
|
||||
"Not Permitted": "Inte Tillåtet",
|
||||
"Not Saved": "Inte Sparad",
|
||||
"Not an Administrator Account": "Inte ett administratörskonto",
|
||||
"NotAuthorizedText1": "Inte Godkänts, Skickar kommandot init med \"auth\",\"ke\", och \"uid\"",
|
||||
"Notes": "Anteckningar",
|
||||
"NotesPlacholder": "Kommentarer om denna kamera.",
|
||||
"Nothing exists": "Det finns inget",
|
||||
"Notification Sound": "Avviserings Ljud",
|
||||
"Notification Video Length": "Avvisera Video Längd",
|
||||
"November": "November",
|
||||
"Number of Days to keep": "Antal Dagar att behålla",
|
||||
"ONVIF Port": "ONVIF-Port",
|
||||
"ONVIF Scanner": "ONVIF-Skanner",
|
||||
"ONVIF": "ONVIF",
|
||||
"ONVIFEventsNotAvailable": "ONVIF Händelser är inte Tillgängligt",
|
||||
"ONVIFnotCompliantProfileT": "Kameran är inte ONVIF Kompatibel",
|
||||
"ONVIFnote": "Upptäck ONVIF enheter i nätverk utanför din egen eller lämna det tomt för att skanna ditt nuvarande nätverk. <br>Användarnamn och Lösenord kan lämnas tomt.",
|
||||
"OR": "ELLER",
|
||||
"Object Detection": "Objektdetektion",
|
||||
"Object Tag": "Objekt-Taggen",
|
||||
"Object": "Objekt",
|
||||
"October": "Oktober",
|
||||
"OpenCV Cascades": "OpenCV Cascades",
|
||||
"Operating Hours": "Öppettider",
|
||||
"Options": "Alternativ",
|
||||
"Order Streams": "Ordna Streams",
|
||||
"Output Method": "Utmatningsmetod",
|
||||
"Password Again": "Lösenordet Igen",
|
||||
"Password": "Lösenord",
|
||||
"Passwords don't match": "Lösenorden matchar inte",
|
||||
"Paste JSON here.": "Klistra in JSON här.",
|
||||
"Path": "Sökväg",
|
||||
"Per Monitor": "Per Kamera",
|
||||
"Permissions": "Behörigheter",
|
||||
"Ping Failed": "Ping Misslyckades",
|
||||
"Please Check Your Settings": "Vänligen, kontrollera dina inställningar",
|
||||
"Please Wait for Completion": "Var vänlig vänta tills det är klart, Beroende på antalet filer markerade kan detta ta lite tid. Uppdatera för att kolla igen.",
|
||||
"Please Wait...": "Vänligen Vänta...",
|
||||
"Plugin": "Plugin",
|
||||
"Points": "Punkter <small>När du lägger till punkter klickar du på kanten av polygonen.</small>",
|
||||
"Pop": "Popup",
|
||||
"Popout Monitor on Event": "Popout Kamera vid Händelse",
|
||||
"Port": "Port",
|
||||
"Pose": "Pose",
|
||||
"Poseidon": "Poseidon",
|
||||
"Position X": "Position X",
|
||||
"Position Y": "Position Y",
|
||||
"Power Video Viewer": "Power Video Viewer",
|
||||
"Power Viewer": "Power Viewer",
|
||||
"Preferences": "Inställningar",
|
||||
"Preset": "Förinställning",
|
||||
"Presets": "Förinställningar",
|
||||
"Preview": "Förhandsgranska",
|
||||
"Previous Video": "Föregående Video",
|
||||
"Primary Engine": "Primär Motor",
|
||||
"Primary Input": "Primär Ingång",
|
||||
"Privileges": "Privilegier",
|
||||
"Probe Size": "Probe Storlek",
|
||||
"Process Already Running": "Processen är Redan Igång",
|
||||
"Process Crashed for Monitor": "Processen har Kraschat för Kamera",
|
||||
"Process Not Running": "Processen Körs Inte",
|
||||
"Process Started": "Processen Startade",
|
||||
"Process Unexpected Exit": "Processen Avslutades Oväntat",
|
||||
"Processor": "Processor",
|
||||
"Profile": "Profil",
|
||||
"Quality": "Kvalitet <small>1 är Hög, 23 är Låg</small>",
|
||||
"Query": "Fråga",
|
||||
"Quick Sync Video": "Quick Sync Video",
|
||||
"RAM": "RAM",
|
||||
"RTMP Stream Flags": "RTMP Stream Flaggor",
|
||||
"RTMP Stream": "RTMP Stream",
|
||||
"RTMP": "RTMP",
|
||||
"RTMPS": "RTMPS",
|
||||
"RTSP Transport": "RTSP Transport",
|
||||
"RTSP": "RTSP",
|
||||
"Range or Single": "Sortiment eller Enstaka",
|
||||
"Raspberry Pi": "Raspberry Pi",
|
||||
"Rate": "Hastighet <small>(FPS)</small>",
|
||||
"Raw H.264 Stream": "Raw H. 264 Stream",
|
||||
"Raw": "Rå",
|
||||
"Reason": "Skäl",
|
||||
"Recent Videos": "Senaste Videor",
|
||||
"Recommended": "Rekommenderas",
|
||||
"Record File Type": "Inspelning Filtyp",
|
||||
"Record Height": "Inspelning Höjd",
|
||||
"Record Video Filter": "Inspelning Video Filter",
|
||||
"Record Width": "Inspelning Bredd",
|
||||
"Record": "Inspelning",
|
||||
"Recorded Buffer": "Inspelning Buffert",
|
||||
"Recording FPS Change on Start": "Inspelning FPS Ändras vid Start",
|
||||
"Recording FPS": "Inspelning FPS",
|
||||
"Recording Flags": "Inspelning Flaggor",
|
||||
"Recording Segment Interval": "Inspelning Segmentet Intervall <small>i minuter</small>",
|
||||
"Recording Timeout": "Inspelning Timeout <small>i Minuter</small>",
|
||||
"Recording Timestamp": "Inspelning Timestamp",
|
||||
"Recording Watermark": "Inspelning Vattenstämpel",
|
||||
"Recording": "Inspelning",
|
||||
"RecordingText": "Det rekommenderas att du ställer in <b>Inspelning filtyp</b> till <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">MP4 -</b> och <b>Video-Codec-enhet</b> för att <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">kopiera eller </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> för din <b>Ingångs Typ</b> är inställd på <b class=\"h_t_text\"></b>.",
|
||||
"Refresh List of Cascades": "Uppdatera Lista på Cascades",
|
||||
"Region Editor": "Regionredigerare",
|
||||
"Region Name": "Region Namn",
|
||||
"Region": "Region",
|
||||
"RegionNote": "Punkter sparas endast när du trycker på <b>Spara</b> i <b>Kamera Inställningar</b> - fönstret.",
|
||||
"Regions": "Regioner",
|
||||
"Remember Me": "Kom Ihåg Mig",
|
||||
"Request": "Begäran",
|
||||
"Require Object to be in Region": "Kräver att Objektet ska vara i Regionen",
|
||||
"Reset Timer": "Återställ Timer",
|
||||
"Restart CRON": "Starta om CRON",
|
||||
"Restart Core": "Starta om Kärnan",
|
||||
"Restart": "Starta om",
|
||||
"Restarting Process": "Startar Om Processen",
|
||||
"Restarting": "Startar om",
|
||||
"Retry Connection": "Återanslut <small>Antalet gånger att misslyckas</small>",
|
||||
"Retrying...": "Försöker igen...",
|
||||
"Right Stop": "Höger Stopp <small>URL-Adress</small>",
|
||||
"Right": "Höger <small>URL-Adress</small>",
|
||||
"Rotate": "Rotera",
|
||||
"S3-Based Network Storage": "S3-Baserat Nätverk Lagring",
|
||||
"SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)",
|
||||
"SFTP Error": "SFTP Fel",
|
||||
"SFTP": "SFTP",
|
||||
"Saturday": "Lördag",
|
||||
"Save Directory": "Spara Katalogen",
|
||||
"Save Events to SQL": "Spara Händelser till SQL",
|
||||
"Save Frames to Events": "Spara Bilder till Händelser",
|
||||
"Save Links to Database": "Spara Länkar till Databasen",
|
||||
"Save Log in SQL": "Spara Logg i SQL <small>Kan fyllas upp snabbt.</small>",
|
||||
"Save as": "Spara som",
|
||||
"Save": "Spara",
|
||||
"Saved Filters": "Sparade Filter",
|
||||
"Saved Presets": "Sparade Förinställningar",
|
||||
"Saved Schedules": "Sparade Scheman",
|
||||
"Scan Settings": "Skanna Inställningar",
|
||||
"Schedule Configuration Not Found": "Schema Konfiguration Finns Inte",
|
||||
"Schedule": "Schema",
|
||||
"Schedules": "Scheman",
|
||||
"Search Base": "Sök Bas",
|
||||
"Search Filter": "Sök Filter",
|
||||
"Search Images": "Sök Bilder",
|
||||
"Search": "Sök",
|
||||
"Search Settings": "Sök Inställningar",
|
||||
"Second stream in feed": "Andra stream i flödet",
|
||||
"Secure": "Säker",
|
||||
"Select a Monitor": "Välj en Kamera",
|
||||
"Send Frames": "Skicka Bilder <small>Bildrutor som skall analyseras</small>",
|
||||
"Separate with commas, no spaces": "Separera med kommatecken, utan blanksteg",
|
||||
"September": "September",
|
||||
"Server URL": "Server-URL",
|
||||
"Set to Watch Only": "För att Titta Endast",
|
||||
"Settings Changed": "Inställningarna Har Ändrats",
|
||||
"Settings": "Inställningar",
|
||||
"SettingsChangedText": "Dina inställningar har sparats och tillämpas. Vissa inställningar kan kräva en uppdatering av denna sida.",
|
||||
"Shinobi Streamer": "Shinobi Streamer",
|
||||
"Shinobi": "Shinobi",
|
||||
"Show Logs": "Visa Loggar",
|
||||
"Show Matrices": "Visa Matriser",
|
||||
"Show Matrix": "Visa Matris",
|
||||
"Show Regions of Interest": "Visa Regioner av Intresse",
|
||||
"Show Stream HUD": "Visa Stream HUD",
|
||||
"Show Thumbnails in Video List": "Visa Miniatyrer i Video Listan",
|
||||
"Silent": "Tyst",
|
||||
"Simple": "Enkel",
|
||||
"Size (mb)": "Storlek (mb)",
|
||||
"Skip Ping": "Hoppa över Ping",
|
||||
"Snapshot Flags": "Snapshot Flaggor",
|
||||
"Snapshot": "Snapshot",
|
||||
"Snapshots": "Ögonblicksbilder",
|
||||
"Sort By": "Sortera Efter",
|
||||
"Space Used": "Utrymme Som Används",
|
||||
"Start Recording": "Starta Inspelningen",
|
||||
"Start Time cannot be empty.": "Starttid kan inte vara tomt.",
|
||||
"Start Time": "Start Tid",
|
||||
"Start": "Start",
|
||||
"Started Building": "Startade Bygga",
|
||||
"Started": "Startade",
|
||||
"Starting": "Startar",
|
||||
"State Configuration Not Found": "Tillstånds Konfiguration Finns Inte",
|
||||
"State Configuration has no monitors associated": "Tillstånds Konfiguration har inga Kameror Associerad",
|
||||
"Status Changed": "Status Ändras",
|
||||
"Reconnect Stream": "Återanslut Stream",
|
||||
"Stop Command": "Stopp Kommando",
|
||||
"Stop URL": "Stopp URL",
|
||||
"Stop": "Stopp",
|
||||
"Stopped": "Stoppad",
|
||||
"Storage Location": "Lagringsplats",
|
||||
"Stream Channel": "Stream Kanal",
|
||||
"Stream Flags": "Stream Flaggor",
|
||||
"Stream Key": "Stream Key",
|
||||
"Stream Timestamp": "Stream Tidstämpel",
|
||||
"Stream Type": "Stream Typ",
|
||||
"Stream Watermark": "Stream Vattenstämpel",
|
||||
"Stream to YouTube Flags": "stream YouTube Flaggor",
|
||||
"Stream to YouTube": "Streama till YouTube",
|
||||
"Stream": "Stream",
|
||||
"StreamText": "<p>Detta avsnitt kommer att utse den primära metoden för att streama ut och dess inställningar. Denna stream kommer att visas i instrumentpanelen. Om du väljer att använda HLS, JPEG, eller MJPEG då kan du snvända denna stream genom andra program.</p><p class=\"h_st_input h_st_jpeg\">Med hjälp av JPEG-stream i huvudsak stänger av den primära-stream och använder ögonblicksbild bin för att få bilder.</p>",
|
||||
"Streamer": "Streamer",
|
||||
"Streams": "Streams",
|
||||
"Subdivision": "Indelning",
|
||||
"Success": "Lyckad",
|
||||
"Sunday": "Söndag",
|
||||
"Superuser Logs": "Superuser Loggar",
|
||||
"Superuser": "Superuser",
|
||||
"Switch on for Still Image": "Slå på för stillbild",
|
||||
"System": "System",
|
||||
"TCP": "TCP",
|
||||
"TV Channel Group": "TV-Kanal Grupp",
|
||||
"TV Channel ID": "TV-Kanal ID",
|
||||
"TV Channel": "TV-Kanal",
|
||||
"Text Box Color": "Text ruta Färg",
|
||||
"Text Color": "Text Färg",
|
||||
"Themes": "Teman",
|
||||
"There are no monitors that you can view with this account.": "Det finns inga Kameror du kan visa med detta konto.",
|
||||
"Thumbnail": "Miniatyr",
|
||||
"Thursday": "Torsdag",
|
||||
"Time Left": "Tid Kvar",
|
||||
"Time Occurred": "Tid Inträffade",
|
||||
"Time": "Tid",
|
||||
"Time-lapse Tool": "Time-lapse Funktion",
|
||||
"Time-lapse": "Time-lapse",
|
||||
"Timelapse Frames Share": "Timelapse Frames Share",
|
||||
"Timelapse Watermark": "Timelapse Vattenstämpel",
|
||||
"Timelapse": "Timelapse",
|
||||
"Timeout Reset on Next Event": "Återställ Timeout på Nästa Händelse",
|
||||
"Timeout Reset on Next Motion": "Återställ Timeout på Nästa Rörelse",
|
||||
"Timeout": "Timeout",
|
||||
"Timezone Offset": "Tidszon Offset",
|
||||
"Timezone": "Tidszon",
|
||||
"Today": "Idag",
|
||||
"Toggle Sidebar": "Växla Sidomeny",
|
||||
"Token": "Token",
|
||||
"Top Left": "Överst Till Vänster",
|
||||
"Top Right": "Överst Till Höger",
|
||||
"Traditional (Watch-Only, Includes Buffer)": "Traditionell (Titta Endast, Inkluderar Buffert)",
|
||||
"Traditional Recording Flags": "Traditionell Inspelning Flaggor",
|
||||
"Traditional Recording": "Traditionell Inspelning",
|
||||
"Train": "Träna",
|
||||
"TrainConfirm": "Är du säker på att du vill börja träna? Detta kan ta mer än 12 timmar med över 500 bilder. Detta kommer att konsumera en stor mängd resurser, som RAM-minne och/eller CPU.",
|
||||
"TrainConfirmStop": "Är du säker på att du vill sluta träna?",
|
||||
"Trainer Engine": "Tränare Motor",
|
||||
"Trigger Blocked": "Trigger Blockerad",
|
||||
"Trigger Camera Groups": "Trigger Kameran Grupper",
|
||||
"Trigger Group to Record": "Trigger Gruppen Inspelning",
|
||||
"Trigger Record": "Trigger Inspelning",
|
||||
"Trigger Successful": "Trigger Lyckad",
|
||||
"Trigger Threshold": "Trigger Tröskel",
|
||||
"Tuesday": "Tisdag",
|
||||
"UDP": "UDP",
|
||||
"URL Stop Timeout": "URL Stopp Timeout <small>Kör Stopp URL efter X millisekunder</small>",
|
||||
"URL": "URL",
|
||||
"US": "OSS",
|
||||
"Unable to Launch": "Det gick inte att Starta",
|
||||
"UnabletoLaunchText": "Vänligen spara ny Kamera först. Försök sedan att köra Regionredigeraren.",
|
||||
"Uniform": "Uniform",
|
||||
"Up Stop": "Stopp upp <small>URL-Adressen,</small>",
|
||||
"Up": "Upp <small>URL-Adressen,</small>",
|
||||
"Update to Development": "Uppdatera till Development",
|
||||
"Update to Master": "Uppdatera till Master",
|
||||
"Update": "Uppdatera",
|
||||
"Upload File": "Ladda Upp Fil",
|
||||
"Uploaders": "Uppladdare",
|
||||
"Use Built-In": "Använd Inbyggd",
|
||||
"Use Global Amazon S3 Video Storage": "Använd Global Amazon S3 Videolagring",
|
||||
"Use Global Backblaze B2 Video Storage": "Använd Global Backblaze B2 Videolagring",
|
||||
"Use Global Wasabi Hot Cloud Storage Video Storage": "Använd Global Wasabi Hot Cloud Storage Videolagring",
|
||||
"Use Global WebDAV Video Storage": "Använd Global WebDAV Videolagring",
|
||||
"Use HTML5 Play Method": "Använd HTML5 Play-Metoden",
|
||||
"Use Max Storage Amount": "Använda Max Lagringstorlek",
|
||||
"Use coProcessor": "Använd coProcessor",
|
||||
"User Not Found": "Användare Finns Inte",
|
||||
"Username": "Användarnamn",
|
||||
"VA-API": "VA-API",
|
||||
"Value": "Värde",
|
||||
"Video Bit Rate": "Video Bitfrekvens",
|
||||
"Video Codec": "Video Codec",
|
||||
"Video Filter": "Video Filter",
|
||||
"Video Finished": "Video Klar",
|
||||
"Video Length (minutes) and Motion Count per video": "Video Längd (minuter) och Rörelser per video",
|
||||
"Video Limit": "Video Gräns",
|
||||
"Video Record Rate": "Videoinspelning Hastighet <small>(FPS)</small>",
|
||||
"Video Set": "Video lagringsplats",
|
||||
"Video Share": "Video Dela",
|
||||
"Video Status": "Video Status",
|
||||
"Video and Time Span (Minutes)": "Video och Tidsspann (Minuter)",
|
||||
"Video stream only from first feed": "Video stream endast från första flödet",
|
||||
"Video streams only": "Endast Video streams",
|
||||
"Video": "Video",
|
||||
"Videos List": "Video Lista",
|
||||
"Videos Merge": "Video Sammanfoga",
|
||||
"Videos": "Videoklipp",
|
||||
"Warning": "Varning",
|
||||
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Hot Cloud Storage Uppladdnings Fel",
|
||||
"Wasabi Hot Cloud Storage": "Wasabi Hot Cloud Storage",
|
||||
"Watch Only": "Endast Titta",
|
||||
"Watch": "Titta",
|
||||
"Watch-Only": "Endast Titta",
|
||||
"Watching": "Tittar på",
|
||||
"Web Page": "Webbsida",
|
||||
"WebDAV": "WebDAV",
|
||||
"WebM (libvpx)": "WebM (libvpx)",
|
||||
"Webdav Error": "Webdav Fel",
|
||||
"WebdavErrorTextCreatingDir": "Kan inte skapa katalogen.",
|
||||
"WebdavErrorTextTryCreatingDir": "Det går inte att spara. Försöker skapa en katalog.",
|
||||
"Webhook URL": "Webhook URL",
|
||||
"Webhook": "Webhook",
|
||||
"Websocket Connected": "Websocket Ansluten",
|
||||
"Websocket Disconnected": "Websocket Frånkopplad",
|
||||
"Websocket": "Websocket",
|
||||
"Wednesday": "Onsdag",
|
||||
"When Detector is Off": "När Detektor är Av",
|
||||
"When Detector is On": "När Detektor är På",
|
||||
"Width": "Bredd",
|
||||
"X Point": "X-Punkt",
|
||||
"Y Point": "Y-Punkt",
|
||||
"Yes": "Ja",
|
||||
"Zip and Download": "Packa och Ladda ner",
|
||||
"Zipping Videos": "Packar Videor",
|
||||
"Zoom In Stop": "Zooma In Stopp <small>URL-Adress</small>",
|
||||
"Zoom In": "Zooma In",
|
||||
"Zoom Out Stop": "Zooma Ut Stopp <small>URL-Adress</small>",
|
||||
"Zoom Out": "Zooma Ut",
|
||||
"\"No Motion\" Detector": "\"No Motion\" - Detektor",
|
||||
"a day": "en dag",
|
||||
"a few seconds": "några sekunder",
|
||||
"a minute": "en minut",
|
||||
"a month": "en månad",
|
||||
"a year": "ett år",
|
||||
"aac (Default)": "aac (Standard)",
|
||||
"aac": "aac",
|
||||
"ac3": "ac3",
|
||||
"accountId": "Konto-ID",
|
||||
"ago": "sedan",
|
||||
"an hour": "en timme",
|
||||
"applicationKey": "Applikations Nyckel",
|
||||
"aws_accessKeyId": "Tillgång Till Nyckel-Id",
|
||||
"aws_secretAccessKey": "Hemlig Nyckel",
|
||||
"bindDN": "bindDN",
|
||||
"blankPassword": "Lämna tomt för att behålla samma lösenord",
|
||||
"clientStreamFailedattemptingReconnect": "Ctream-kontrollen på klientsidan misslyckades, försökte återansluta.",
|
||||
"coProcess Crashed for Monitor": "coProcess har Kraschat för Kamera",
|
||||
"coProcess Unexpected Exit": "coProcess Oväntad Utgång",
|
||||
"coProcessor Started": "coProcessor Igång",
|
||||
"coProcessor Stopped": "coProcessor Stoppad",
|
||||
"coProcessor": "coProcessor",
|
||||
"coProcessorTextStarted": "coProcessor har startad för CPU bara utgångar.",
|
||||
"coProcessorTextStopped": "coProcessor har upphört.",
|
||||
"confirmDeleteFilter": "Vill du ta bort detta filter? Du kan inte återställa det.",
|
||||
"copy": "kopiera",
|
||||
"cuvid": "cuvid (NVIDIA NVENC)",
|
||||
"days": "dagar",
|
||||
"deleteMonitorStateText1": "Vill du ta bort denna Monitor Preset?",
|
||||
"deleteMonitorStateText2": "Vill du ta bort denna Monitor är Förinställd?",
|
||||
"drm": "DRM-objekt dela",
|
||||
"dropBoxSuccess": "Lyckad! Filer har sparats i din Dropbox.",
|
||||
"dxva2": "dxva2 (DirectX Video, Windows)",
|
||||
"failedLoginText1": "Du har misslyckats med att logga in för många gånger. Du måste vänta i 15 minuter innan du kan försöka igen.",
|
||||
"failedLoginText2": "Vänligen kontrollera dina inloggningsuppgifter.",
|
||||
"flv": "flv",
|
||||
"for Global Access": "för Global Tillgång",
|
||||
"h264_cuvid": "H.264 CUVID",
|
||||
"h264_mmal": "H.264 (Raspberry Pi)",
|
||||
"h264_nvenc": "H.264 NVENC (NVIDIA HW Accel)",
|
||||
"h264_omx": "H.264 openMAX (Raspberry Pi)",
|
||||
"h264_qsv": "H.264 (Quick Sync Video)",
|
||||
"h264_vaapi": "H.264 VA-API (Intel HW Accel)",
|
||||
"h265BrowserText1": "Om du försöker spela upp en H.265 fil, kan du behöva ladda ner den och öppna den i ett annat program, t.ex VLC.",
|
||||
"hevc_cuvid": "H.265 CUVID",
|
||||
"hevc_nvenc": "H.265 NVENC (NVIDIA HW Accel)",
|
||||
"hevc_qsv": "H.265 (Quick Sync Video)",
|
||||
"hevc_vaapi": "H.265 VA-API (Intel HW Accel)",
|
||||
"hours": "timmar",
|
||||
"hwaccel": "Accelerator Motor",
|
||||
"hwaccel_device": "HWAccel Enhet",
|
||||
"hwaccel_vcodec": "Video Decoder",
|
||||
"in Days": "i Dagar",
|
||||
"in": "i",
|
||||
"keyId": "Nyckel-ID",
|
||||
"libmp3lame": "libmp3lame",
|
||||
"libopus": "libopus",
|
||||
"libvorbis (Default)": "libvorbis (Standard)",
|
||||
"libvpx (Default)": "libvpx (Standard)",
|
||||
"libvpx-vp9": "libvpx-vp9",
|
||||
"libx264 (Default)": "libx264 (Standard)",
|
||||
"libx264": "libx264",
|
||||
"libx265": "libx265",
|
||||
"migrateText1": "<b>Ingångs Typ</b> inte kunde tolkas. Ange det manuellt.",
|
||||
"minutes": "minuter",
|
||||
"mjpeg_cuvid": "MJPEG CUVID",
|
||||
"modifyVideoText1": "Metoden inte existerar. Kontrollera att det sista värdet av URL:en är inte tom.",
|
||||
"monSavedButNotCopied": "Din Kamera har sparats men inte kopieras till en annan Kamera.",
|
||||
"monitorEditFailedMaxReached": "Ditt konto har uppnått det maximala antalet kameror som kan skapas. Kontakta en administratör om du vill att detta ändras.",
|
||||
"monitorEditText1": "Ogiltiga Data, Kontrollera för att se att detta är en giltig import sträng.",
|
||||
"monitorEditText2": "Ogiltig Informations Sträng. Kontrollera för att se att det är en JSON-sträng och inte ett objekt som skickats.",
|
||||
"monitorGetText1": "Ofullständig begäran, ta bort sista snedstreck i URL-adressen eller sätt ett acceptabelt värde.",
|
||||
"months": "månader",
|
||||
"mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
|
||||
"mpeg2_qsv": "MPEG2 (Quick Sync Video)",
|
||||
"mpeg4_cuvid": "MPEG4 CUVID",
|
||||
"mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
|
||||
"noSpecialCharacters": "Inga mellanslag eller specialtecken.",
|
||||
"notPermitted1": "Denna åtgärd är inte tillåtet av administratören för ditt konto.'",
|
||||
"on Error": "vid Fel",
|
||||
"on": "på",
|
||||
"openImagesDownloadConfirm": "Är du säker på att du vill börja ladda ner bilder och bounding boxes (förinställda Matriser) från OpenImages?",
|
||||
"openImagesDownloadConfirmStop": "Är du säker på att du vill sluta träna?",
|
||||
"opencvCascadesText": "Om du ser något här sedan är det bara att hämta paketet på <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascades</a>. Släpp dem i <code>plugins/opencv/kaskader</code> tryck sedan på uppdatera <i class=\"fa fa-retweet\"></i>.",
|
||||
"possibleInternalError": "Möjligt Internt Fel",
|
||||
"postDataBroken": "Kontrollera formatet JSON. Se till att den är sträng och definieras under \"uppgifter\"",
|
||||
"powerVideoEventLimit": "Du har satt en hög gräns för händelser. Är du säker på att du vill göra denna begäran?",
|
||||
"privateKey": "Privat Nyckel",
|
||||
"qsv": "qsv",
|
||||
"sizePurgeLockedText": "The Size Purge Lock (deleteOverMax) verkar ha misslyckats att låsa upp. Låser upp nu...",
|
||||
"skipPingText1": "Prova att sätta \"Hoppa över Ping\" till Ja.",
|
||||
"startUpText0": "Kontroll av disk som används..",
|
||||
"startUpText1": "Klart att kontrollera hårddisken som används.",
|
||||
"startUpText2": "alla användare kontrolleras, vänta med att stänga öppna filer och ta bort filer över gränsen för antal användare",
|
||||
"startUpText3": "väntar, ger oavslutade video lite mer tid. 3 sekunder.",
|
||||
"startUpText4": "Startar Kameror... Var God Vänta...",
|
||||
"startUpText5": "Shinobi är redo.",
|
||||
"startUpText6": "Föräldralösa Videor hittade och infogade",
|
||||
"superAdminText": "\"super.json\" existerar inte. Vänligen byt namn \"super.sample.json\" till en \"super.json\".",
|
||||
"superAdminTitle": "Shinobi : Super Admin",
|
||||
"total": "totalt",
|
||||
"updateKeyText1": "\"updateKey\" saknas \"conf.json\", kan inte göra uppdateringar på detta sätt tills du lägger till den.",
|
||||
"updateKeyText2": "\"updateKey\" är felaktig.",
|
||||
"vaapi": "vaapi (VA-API)",
|
||||
"vda": "vda (Apple VDA Acceleration)",
|
||||
"vdpau": "vdpau",
|
||||
"videotoolbox": "videotoolbox",
|
||||
"vp8_cuvid": "VP8 NVENC (NVIDIA HW Accel)",
|
||||
"vp8_qsv": "VP8 (Quick Sync Video)",
|
||||
"vp9_cuvid": "VP9 NVENC (NVIDIA HW Accel)",
|
||||
"No Sound": "Inget Ljud",
|
||||
"years": "år"
|
||||
}
|
255
libs/auth.js
255
libs/auth.js
|
@ -5,109 +5,184 @@ module.exports = function(s,config,lang){
|
|||
s.superUsersApi = {}
|
||||
s.factorAuth = {}
|
||||
s.failedLoginAttempts = {}
|
||||
//auth handler
|
||||
//params = parameters
|
||||
//cb = callback
|
||||
//res = response, only needed for express (http server)
|
||||
//request = request, only needed for express (http server)
|
||||
s.auth = function(params,cb,res,req){
|
||||
//
|
||||
var getUserByUid = function(params,columns,callback){
|
||||
if(!columns)columns = '*'
|
||||
s.sqlQuery(`SELECT ${columns} FROM Users WHERE uid=? AND ke=?`,[params.uid,params.ke],function(err,r){
|
||||
if(!r)r = []
|
||||
var user = r[0]
|
||||
callback(err,user)
|
||||
})
|
||||
}
|
||||
var getUserBySessionKey = function(params,callback){
|
||||
s.sqlQuery('SELECT * FROM Users WHERE auth=? AND ke=?',[params.auth,params.ke],function(err,r){
|
||||
if(!r)r = []
|
||||
var user = r[0]
|
||||
callback(err,user)
|
||||
})
|
||||
}
|
||||
var loginWithUsernameAndPassword = function(params,columns,callback){
|
||||
if(!columns)columns = '*'
|
||||
s.sqlQuery(`SELECT ${columns} FROM Users WHERE mail=? AND (pass=? OR pass=?) LIMIT 1`,[params.username,params.password,s.createHash(params.password)],function(err,r){
|
||||
if(!r)r = []
|
||||
var user = r[0]
|
||||
callback(err,user)
|
||||
})
|
||||
}
|
||||
var getApiKey = function(params,columns,callback){
|
||||
if(!columns)columns = '*'
|
||||
s.sqlQuery(`SELECT ${columns} FROM API WHERE code=? AND ke=?`,[params.auth,params.ke],function(err,r){
|
||||
if(!r)r = []
|
||||
var apiKey = r[0]
|
||||
callback(err,apiKey)
|
||||
})
|
||||
}
|
||||
var loginWithApiKey = function(params,callback){
|
||||
getApiKey(params,'*',function(err,apiKey){
|
||||
var isSessionKey = false
|
||||
if(apiKey){
|
||||
var sessionKey = params.auth
|
||||
createSession(apiKey,{
|
||||
auth: sessionKey,
|
||||
permissions: s.parseJSON(apiKey.details),
|
||||
details: {}
|
||||
})
|
||||
getUserByUid(apiKey,'mail,details',function(err,user){
|
||||
if(user){
|
||||
try{
|
||||
editSession({
|
||||
auth: sessionKey
|
||||
},{
|
||||
mail: user.mail,
|
||||
details: s.parseJSON(user.details),
|
||||
lang: s.getLanguageFile(user.details.lang)
|
||||
})
|
||||
}catch(er){
|
||||
console.log('FAILED TO EDIT',er)
|
||||
}
|
||||
}
|
||||
callback(err,s.api[params.auth])
|
||||
})
|
||||
}else{
|
||||
getUserBySessionKey(params,function(err,user){
|
||||
if(user){
|
||||
isSessionKey = true
|
||||
createSession(apiKey,{
|
||||
details: JSON.parse(user.details),
|
||||
permissions: {}
|
||||
})
|
||||
callback(err,user,isSessionKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
var createSession = function(user,additionalData){
|
||||
if(user){
|
||||
var generatedId
|
||||
if(!additionalData)additionalData = {}
|
||||
if(!user.ip)user.ip = '0.0.0.0'
|
||||
if(!user.auth && !user.code){
|
||||
generatedId = s.gid(20)
|
||||
}else{
|
||||
generatedId = user.auth || user.code
|
||||
}
|
||||
user.details = s.parseJSON(user.details)
|
||||
user.permissions = {}
|
||||
s.api[generatedId] = Object.assign(user,additionalData)
|
||||
return generatedId
|
||||
}
|
||||
}
|
||||
var editSession = function(user,additionalData){
|
||||
if(user){
|
||||
if(!additionalData)additionalData = {}
|
||||
Object.keys(additionalData).forEach(function(value,key){
|
||||
s.api[user.auth][key] = value
|
||||
})
|
||||
}
|
||||
}
|
||||
var failHttpAuthentication = function(res,req,message){
|
||||
if(!message)message = lang['Not Authorized']
|
||||
res.end(s.prettyPrint({
|
||||
ok: false,
|
||||
msg: message
|
||||
}))
|
||||
}
|
||||
var resetActiveSessionTimer = function(activeSession){
|
||||
if(activeSession){
|
||||
clearTimeout(activeSession.timeout)
|
||||
activeSession.timeout = setTimeout(function(){
|
||||
delete(activeSession)
|
||||
},1000 * 60 * 5)
|
||||
}
|
||||
}
|
||||
s.auth = function(params,onSuccessComplete,res,req){
|
||||
if(req){
|
||||
//express (http server) use of auth function
|
||||
params.ip=req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
var failed=function(){
|
||||
if(!req.ret){req.ret={ok:false}}
|
||||
req.ret.msg=lang['Not Authorized'];
|
||||
res.end(s.s(req.ret));
|
||||
params.ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
|
||||
var onFail = function(message){
|
||||
failHttpAuthentication(res,req,message)
|
||||
}
|
||||
}else{
|
||||
//socket.io use of auth function
|
||||
var failed = function(){
|
||||
var onFail = function(){
|
||||
//maybe log
|
||||
}
|
||||
}
|
||||
var clearAfterTime=function(){
|
||||
//remove temp key from memory
|
||||
clearTimeout(s.api[params.auth].timeout)
|
||||
s.api[params.auth].timeout=setTimeout(function(){
|
||||
delete(s.api[params.auth])
|
||||
},1000*60*5)
|
||||
}
|
||||
//check IP address of connecting user
|
||||
var finish=function(user){
|
||||
if(s.api[params.auth].ip.indexOf('0.0.0.0')>-1||s.api[params.auth].ip.indexOf(params.ip)>-1){
|
||||
var onSuccess = function(user){
|
||||
var activeSession = s.api[params.auth]
|
||||
if(
|
||||
activeSession &&
|
||||
(
|
||||
activeSession.ip.indexOf('0.0.0.0') > -1 ||
|
||||
activeSession.ip.indexOf(params.ip) > -1
|
||||
)
|
||||
){
|
||||
if(!user.lang){
|
||||
var details = s.parseJSON(user.details).lang
|
||||
user.lang = s.getDefinitonFile(user.details.lang) || s.copySystemDefaultLanguage()
|
||||
}
|
||||
cb(user);
|
||||
onSuccessComplete(user)
|
||||
}else{
|
||||
failed();
|
||||
onFail()
|
||||
}
|
||||
}
|
||||
//check if auth key is user's temporary session key
|
||||
if(s.group[params.ke]&&s.group[params.ke].users&&s.group[params.ke].users[params.auth]){
|
||||
s.group[params.ke].users[params.auth].permissions={};
|
||||
if(!s.group[params.ke].users[params.auth].lang){
|
||||
s.group[params.ke].users[params.auth].lang = s.copySystemDefaultLanguage()
|
||||
if(s.group[params.ke] && s.group[params.ke].users && s.group[params.ke].users[params.auth]){
|
||||
var activeSession = s.group[params.ke].users[params.auth]
|
||||
activeSession.permissions = {}
|
||||
if(!activeSession.lang){
|
||||
activeSession.lang = s.copySystemDefaultLanguage()
|
||||
}
|
||||
cb(s.group[params.ke].users[params.auth])
|
||||
onSuccessComplete(activeSession)
|
||||
}else{
|
||||
//check if key is already in memory to save query time
|
||||
if(s.api[params.auth]&&s.api[params.auth].details){
|
||||
finish(s.api[params.auth]);
|
||||
if(s.api[params.auth].timeout){
|
||||
clearAfterTime()
|
||||
if(s.api[params.auth] && s.api[params.auth].details){
|
||||
var activeSession = s.api[params.auth]
|
||||
onSuccess(activeSession)
|
||||
if(activeSession.timeout){
|
||||
resetActiveSessionTimer(activeSession)
|
||||
}
|
||||
}else{
|
||||
//no key in memory, query db to see if key exists
|
||||
//check if using username and password in plain text or md5
|
||||
if(params.username&¶ms.username!==''&¶ms.password&¶ms.password!==''){
|
||||
s.sqlQuery('SELECT * FROM Users WHERE mail=? AND (pass=? OR pass=?)',[params.username,params.password,s.createHash(params.password)],function(err,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0];
|
||||
r.ip='0.0.0.0';
|
||||
r.auth = s.gid(20);
|
||||
params.auth = r.auth;
|
||||
r.details=JSON.parse(r.details);
|
||||
r.permissions = {};
|
||||
s.api[r.auth]=r;
|
||||
clearAfterTime();
|
||||
finish(r);
|
||||
if(params.username && params.username !== '' && params.password && params.password !== ''){
|
||||
loginWithUsernameAndPassword(params,'*',function(err,user){
|
||||
if(user){
|
||||
params.auth = user.auth
|
||||
createSession(user)
|
||||
resetActiveSessionTimer(s.api[params.auth])
|
||||
onSuccess(user)
|
||||
}else{
|
||||
failed();
|
||||
onFail()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
//not using plain login
|
||||
s.sqlQuery('SELECT * FROM API WHERE code=? AND ke=?',[params.auth,params.ke],function(err,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0];
|
||||
s.api[params.auth]={ip:r.ip,uid:r.uid,ke:r.ke,permissions:JSON.parse(r.details),details:{}};
|
||||
s.sqlQuery('SELECT mail,details FROM Users WHERE uid=? AND ke=?',[r.uid,r.ke],function(err,rr){
|
||||
if(rr&&rr[0]){
|
||||
rr=rr[0];
|
||||
try{
|
||||
s.api[params.auth].mail=rr.mail
|
||||
s.api[params.auth].details=JSON.parse(rr.details)
|
||||
s.api[params.auth].lang=s.getLanguageFile(s.api[params.auth].details.lang)
|
||||
}catch(er){}
|
||||
}
|
||||
finish(s.api[params.auth]);
|
||||
loginWithApiKey(params,function(err,user,isSessionKey){
|
||||
if(isSessionKey)resetActiveSessionTimer(s.api[params.auth])
|
||||
if(user){
|
||||
createSession(user,{
|
||||
auth: params.auth
|
||||
})
|
||||
onSuccess(s.api[params.auth])
|
||||
}else{
|
||||
s.sqlQuery('SELECT * FROM Users WHERE auth=? AND ke=?',[params.auth,params.ke],function(err,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0];
|
||||
r.ip='0.0.0.0'
|
||||
s.api[params.auth]=r
|
||||
s.api[params.auth].details=JSON.parse(r.details)
|
||||
s.api[params.auth].permissions={}
|
||||
clearAfterTime()
|
||||
finish(r)
|
||||
}else{
|
||||
failed();
|
||||
}
|
||||
})
|
||||
onFail()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -121,8 +196,10 @@ module.exports = function(s,config,lang){
|
|||
var adminUsersSelected = null
|
||||
try{
|
||||
var success = function(){
|
||||
var chosenConfig = config
|
||||
if(req && res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
chosenConfig = s.getConfigWithBranding(req.hostname)
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
var ip = req.headers['cf-connecting-ip']||req.headers["CF-Connecting-IP"]||req.headers["'x-forwarded-for"]||req.connection.remoteAddress;
|
||||
var resp = {
|
||||
ok: userFound,
|
||||
|
@ -141,9 +218,9 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
callback({
|
||||
ip : ip,
|
||||
$user:userSelected,
|
||||
users:adminUsersSelected,
|
||||
config:config,
|
||||
$user: userSelected,
|
||||
users: adminUsersSelected,
|
||||
config: chosenConfig,
|
||||
lang:lang
|
||||
})
|
||||
}
|
||||
|
@ -199,4 +276,18 @@ module.exports = function(s,config,lang){
|
|||
return false
|
||||
}
|
||||
}
|
||||
s.basicOrApiAuthentication = function(username,password,callback){
|
||||
var splitUsername = username.split('@')
|
||||
if(splitUsername[1] && splitUsername[1].toLowerCase().indexOf('shinobi') > -1){
|
||||
getApiKey({
|
||||
auth: splitUsername[0],
|
||||
ke: password
|
||||
},'ke,uid',callback)
|
||||
}else{
|
||||
loginWithUsernameAndPassword({
|
||||
username: username,
|
||||
password: password
|
||||
},'ke,uid',callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
var moment = require('moment');
|
||||
var crypto = require('crypto');
|
||||
var exec = require('child_process').exec;
|
||||
var fs = require('fs')
|
||||
var moment = require('moment')
|
||||
var crypto = require('crypto')
|
||||
var exec = require('child_process').exec
|
||||
var spawn = require('child_process').spawn;
|
||||
var events = require('events');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var events = require('events')
|
||||
var http = require('http')
|
||||
var https = require('https')
|
||||
const async = require("async")
|
||||
module.exports = function(s,config){
|
||||
//kill any ffmpeg running
|
||||
s.ffmpegKill=function(){
|
||||
|
@ -75,7 +77,12 @@ module.exports = function(s,config){
|
|||
break;
|
||||
}
|
||||
//load camera controller vars
|
||||
s.nameToTime=function(x){x=x.split('.')[0].split('T'),x[1]=x[1].replace(/-/g,':');x=x.join(' ');return x;}
|
||||
s.nameToTime=function(x){
|
||||
x = x.split('.')[0].split('T')
|
||||
if(x[1])x[1] = x[1].replace(/-/g,':')
|
||||
x = x.join(' ')
|
||||
return x
|
||||
}
|
||||
s.ratio=function(width,height,ratio){ratio = width / height;return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';}
|
||||
s.randomNumber=function(x){
|
||||
if(!x){x=10};
|
||||
|
@ -206,7 +213,7 @@ module.exports = function(s,config){
|
|||
}
|
||||
return url
|
||||
}
|
||||
s.file=function(x,e){
|
||||
s.file = function(x,e,callback){
|
||||
if(!e){e={}};
|
||||
switch(x){
|
||||
case'size':
|
||||
|
@ -214,19 +221,25 @@ module.exports = function(s,config){
|
|||
break;
|
||||
case'delete':
|
||||
if(!e){return false;}
|
||||
return exec('rm -f '+e,{detached: true});
|
||||
return exec('rm -f '+e,{detached: true},function(err){
|
||||
if(callback)callback(err)
|
||||
})
|
||||
break;
|
||||
case'deleteFolder':
|
||||
if(!e){return false;}
|
||||
return exec('rm -rf '+e,{detached: true});
|
||||
exec('rm -rf '+e,{detached: true},function(err){
|
||||
if(callback)callback(err)
|
||||
})
|
||||
break;
|
||||
case'deleteFiles':
|
||||
if(!e.age_type){e.age_type='min'};if(!e.age){e.age='1'};
|
||||
exec('find '+e.path+' -type f -c'+e.age_type+' +'+e.age+' -exec rm -f {} +',{detached: true});
|
||||
exec('find '+e.path+' -type f -c'+e.age_type+' +'+e.age+' -exec rm -f {} +',{detached: true},function(err){
|
||||
if(callback)callback(err)
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.createTimeout = function(timeoutVar,timeoutLength,defaultLength,multiplier,callback){
|
||||
s.createTimeout = function(timeoutVar,parentVar,timeoutLength,defaultLength,multiplier,callback){
|
||||
var theTimeout
|
||||
if(!multiplier)multiplier = 1000 * 60
|
||||
if(!timeoutLength || timeoutLength === ''){
|
||||
|
@ -234,12 +247,43 @@ module.exports = function(s,config){
|
|||
}else{
|
||||
theTimeout = parseFloat(timeoutLength) * multiplier
|
||||
}
|
||||
clearTimeout(timeoutVar)
|
||||
timeoutVar = setTimeout(function(){
|
||||
clearTimeout(timeoutVar)
|
||||
delete(timeoutVar)
|
||||
clearTimeout(parentVar[timeoutVar])
|
||||
parentVar[timeoutVar] = setTimeout(function(){
|
||||
clearTimeout(parentVar[timeoutVar])
|
||||
delete(parentVar[timeoutVar])
|
||||
if(callback)callback()
|
||||
},theTimeout)
|
||||
return parentVar[timeoutVar]
|
||||
}
|
||||
s.handleFolderError = function(err){
|
||||
if(err){
|
||||
switch(err.code){
|
||||
case'EEXIST':
|
||||
break;
|
||||
default:
|
||||
console.log(err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
s.isCorrectFilenameSyntax = function(string){
|
||||
return RegExp('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]').test(string)
|
||||
}
|
||||
var readFile = async.queue(function(filename, callback) {
|
||||
fs.readFile(filename,"utf-8",callback)
|
||||
}, 4);
|
||||
s.readFile = function(filename, callback){
|
||||
return readFile.push(filename, callback)
|
||||
}
|
||||
var fileStats = async.queue(function(filename, callback) {
|
||||
fs.stat(filename,callback)
|
||||
}, 4);
|
||||
s.fileStats = function(filename, callback){
|
||||
return fileStats.push(filename, callback)
|
||||
}
|
||||
s.kilobyteToMegabyte = function(kb,places){
|
||||
if(!places)places = 2
|
||||
return (kb/1000000).toFixed(places)
|
||||
}
|
||||
Object.defineProperty(Array.prototype, 'chunk', {
|
||||
value: function(chunkSize){
|
||||
|
|
20
libs/branding.js
Normal file
20
libs/branding.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
module.exports = function(s,config,lang,app,io){
|
||||
if(config.showPoweredByShinobi === undefined){config.showPoweredByShinobi=true}
|
||||
if(config.poweredByShinobi === undefined){config.poweredByShinobi='Powered by Shinobi.Systems'}
|
||||
if(config.poweredByShinobiClass === undefined){config.poweredByShinobiClass='margin:15px 0 0 0;text-align:center;color:#777;font-family: sans-serif;text-transform: uppercase;letter-spacing: 3;font-size: 8pt;'}
|
||||
if(config.webPageTitle === undefined){config.webPageTitle='Shinobi'}
|
||||
if(config.showLoginCardHeader === undefined){config.showLoginCardHeader=true}
|
||||
if(config.webFavicon === undefined){config.webFavicon='libs/img/icon/favicon.ico'}
|
||||
if(config.logoLocation76x76 === undefined){config.logoLocation76x76='libs/img/icon/apple-touch-icon-76x76.png'}
|
||||
if(config.logoLocation76x76Link === undefined){config.logoLocation76x76Link='https://shinobi.video'}
|
||||
if(config.logoLocation76x76Style === undefined){config.logoLocation76x76Style='border-radius:50%'}
|
||||
if(config.showLoginSelector === undefined){config.showLoginSelector=true}
|
||||
|
||||
s.getConfigWithBranding = function(domain){
|
||||
var configCopy = Object.assign({},config)
|
||||
if(config.brandingConfig && config.brandingConfig[domain]){
|
||||
return Object.assign(configCopy,config.brandingConfig[domain])
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
|
@ -13,7 +13,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
console.log(lang.Shinobi+' - CHILD NODE PORT : '+config.childNodes.port);
|
||||
});
|
||||
s.debugLog('childNodeWebsocket.attach(childNodeServer)')
|
||||
childNodeWebsocket.attach(childNodeServer);
|
||||
childNodeWebsocket.attach(childNodeServer,{
|
||||
path:'/socket.io',
|
||||
transports: ['websocket']
|
||||
});
|
||||
//send data to child node function (experimental)
|
||||
s.cx = function(z,y,x){
|
||||
if(!z.mid && !z.d){
|
||||
|
@ -27,10 +30,12 @@ module.exports = function(s,config,lang,app,io){
|
|||
//child Node Websocket
|
||||
childNodeWebsocket.on('connection', function (cn) {
|
||||
//functions for dispersing work to child servers;
|
||||
var ipAddress
|
||||
cn.on('c',function(d){
|
||||
if(config.childNodes.key.indexOf(d.socketKey) > -1){
|
||||
if(!cn.shinobi_child&&d.f=='init'){
|
||||
cn.ip = cn.request.connection.remoteAddress.replace('::ffff:','')+':'+d.port
|
||||
ipAddress = cn.request.connection.remoteAddress.replace('::ffff:','')+':'+d.port
|
||||
cn.ip = ipAddress
|
||||
cn.shinobi_child = 1
|
||||
tx = function(z){
|
||||
cn.emit('c',z)
|
||||
|
@ -38,8 +43,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
if(!s.childNodes[cn.ip]){
|
||||
s.childNodes[cn.ip] = {}
|
||||
};
|
||||
s.childNodes[cn.ip].dead = false
|
||||
s.childNodes[cn.ip].cnid = cn.id
|
||||
s.childNodes[cn.ip].cpu = 0
|
||||
s.childNodes[cn.ip].ip = ipAddress
|
||||
s.childNodes[cn.ip].activeCameras = {}
|
||||
d.availableHWAccels.forEach(function(accel){
|
||||
if(config.availableHWAccels.indexOf(accel) === -1)config.availableHWAccels.push(accel)
|
||||
|
@ -52,13 +59,16 @@ module.exports = function(s,config,lang,app,io){
|
|||
}else{
|
||||
switch(d.f){
|
||||
case'cpu':
|
||||
s.childNodes[cn.ip].cpu = d.cpu;
|
||||
s.childNodes[ipAddress].cpu = d.cpu;
|
||||
break;
|
||||
case'sql':
|
||||
s.sqlQuery(d.query,d.values,function(err,rows){
|
||||
cn.emit('c',{f:'sqlCallback',rows:rows,err:err,callbackId:d.callbackId});
|
||||
});
|
||||
break;
|
||||
case'clearCameraFromActiveList':
|
||||
if(s.childNodes[ipAddress])delete(s.childNodes[ipAddress].activeCameras[d.ke + d.id])
|
||||
break;
|
||||
case'camera':
|
||||
s.camera(d.mode,d.data)
|
||||
break;
|
||||
|
@ -69,24 +79,54 @@ module.exports = function(s,config,lang,app,io){
|
|||
if(!d.mon || !d.data)return console.log('LOG DROPPED',d.mon,d.data);
|
||||
s.userLog(d.mon,d.data)
|
||||
break;
|
||||
case'created_file_chunk':
|
||||
if(!s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename]){
|
||||
d.dir = s.getVideoDirectory(s.group[d.ke].mon_conf[d.mid])
|
||||
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(d.dir+d.filename)
|
||||
case'open_timelapse_file_transfer':
|
||||
var location = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/`
|
||||
if(!fs.existsSync(location)){
|
||||
fs.mkdirSync(location)
|
||||
}
|
||||
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
|
||||
break;
|
||||
case'created_file':
|
||||
if(!s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename]){
|
||||
case'created_timelapse_file_chunk':
|
||||
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
|
||||
var dir = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/`
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(dir+d.filename)
|
||||
}
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
|
||||
break;
|
||||
case'created_timelapse_file':
|
||||
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
|
||||
return console.log('FILE NOT EXIST')
|
||||
}
|
||||
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename].end();
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end()
|
||||
tx({
|
||||
f: 'deleteTimelapseFrame',
|
||||
file: d.filename,
|
||||
currentDate: d.currentDate,
|
||||
d: d.d, //monitor config
|
||||
ke: d.ke,
|
||||
mid: d.mid
|
||||
})
|
||||
s.insertTimelapseFrameDatabaseRow({
|
||||
ke: d.ke
|
||||
},d.queryInfo)
|
||||
break;
|
||||
case'created_file_chunk':
|
||||
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
|
||||
d.dir = s.getVideoDirectory(s.group[d.ke].rawMonitorConfigurations[d.mid])
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(d.dir+d.filename)
|
||||
}
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
|
||||
break;
|
||||
case'created_file':
|
||||
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
|
||||
return console.log('FILE NOT EXIST')
|
||||
}
|
||||
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end();
|
||||
tx({
|
||||
f:'delete',
|
||||
file:d.filename,
|
||||
ke:d.ke,
|
||||
mid:d.mid
|
||||
});
|
||||
})
|
||||
s.txWithSubPermissions({
|
||||
f:'video_build_success',
|
||||
hrefNoAuth:'/videos/'+d.ke+'/'+d.mid+'/'+d.filename,
|
||||
|
@ -115,28 +155,39 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.purgeDiskForGroup(d)
|
||||
//send new diskUsage values
|
||||
s.setDiskUsedForGroup(d,insert.filesizeMB)
|
||||
clearTimeout(s.group[d.ke].mon[d.mid].recordingChecker)
|
||||
clearTimeout(s.group[d.ke].mon[d.mid].streamChecker)
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.mid].recordingChecker)
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.mid].streamChecker)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
cn.on('disconnect',function(){
|
||||
console.log('childNodeWebsocket.disconnect')
|
||||
|
||||
if(s.childNodes[cn.ip]){
|
||||
var activeCameraKeys = Object.keys(s.childNodes[cn.ip].activeCameras)
|
||||
activeCameraKeys.forEach(function(key){
|
||||
var monitor = s.childNodes[cn.ip].activeCameras[key]
|
||||
s.camera('stop',s.cleanMonitorObject(monitor))
|
||||
delete(s.group[monitor.ke].mon[monitor.mid].childNode)
|
||||
delete(s.group[monitor.ke].mon[monitor.mid].childNodeId)
|
||||
setTimeout(function(){
|
||||
s.camera(monitor.mode,s.cleanMonitorObject(monitor))
|
||||
},1300)
|
||||
})
|
||||
delete(s.childNodes[cn.ip]);
|
||||
console.log('childNodeWebsocket.disconnect',ipAddress)
|
||||
if(s.childNodes[ipAddress]){
|
||||
var monitors = Object.values(s.childNodes[ipAddress].activeCameras)
|
||||
if(monitors && monitors[0]){
|
||||
var loadCompleted = 0
|
||||
var loadMonitor = function(monitor){
|
||||
setTimeout(function(){
|
||||
var mode = monitor.mode + ''
|
||||
var cleanMonitor = s.cleanMonitorObject(monitor)
|
||||
s.camera('stop',Object.assign(cleanMonitor,{}))
|
||||
delete(s.group[monitor.ke].activeMonitors[monitor.mid].childNode)
|
||||
delete(s.group[monitor.ke].activeMonitors[monitor.mid].childNodeId)
|
||||
setTimeout(function(){
|
||||
s.camera(mode,cleanMonitor)
|
||||
++loadCompleted
|
||||
if(monitors[loadCompleted]){
|
||||
loadMonitor(monitors[loadCompleted])
|
||||
}
|
||||
},1000)
|
||||
},2000)
|
||||
}
|
||||
loadMonitor(monitors[loadCompleted])
|
||||
}
|
||||
s.childNodes[ipAddress].activeCameras = {}
|
||||
s.childNodes[ipAddress].dead = true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -144,7 +195,9 @@ module.exports = function(s,config,lang,app,io){
|
|||
//setup Child for childNodes
|
||||
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
|
||||
s.connected = false;
|
||||
childIO = require('socket.io-client')('ws://'+config.childNodes.host);
|
||||
childIO = require('socket.io-client')('ws://'+config.childNodes.host,{
|
||||
transports: ['websocket']
|
||||
});
|
||||
s.cx = function(x){x.socketKey = config.childNodes.key;childIO.emit('c',x)}
|
||||
s.tx = function(x,y){s.cx({f:'s.tx',data:x,to:y})}
|
||||
s.userLog = function(x,y){s.cx({f:'s.userLog',mon:x,data:y})}
|
||||
|
@ -162,9 +215,9 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
setInterval(function(){
|
||||
s.cpuUsage(function(cpu){
|
||||
io.emit('c',{f:'cpu',cpu:parseFloat(cpu)});
|
||||
s.cx({f:'cpu',cpu:parseFloat(cpu)})
|
||||
})
|
||||
},2000);
|
||||
},2000)
|
||||
childIO.on('connect', function(d){
|
||||
console.log('CHILD CONNECTION SUCCESS')
|
||||
s.cx({
|
||||
|
@ -188,17 +241,22 @@ module.exports = function(s,config,lang,app,io){
|
|||
break;
|
||||
case'kill':
|
||||
s.initiateMonitorObject(d.d);
|
||||
s.cameraDestroy(s.group[d.d.ke].mon[d.d.id].spawn,d.d)
|
||||
s.cameraDestroy(s.group[d.d.ke].activeMonitors[d.d.id].spawn,d.d)
|
||||
var childNodeIp = s.group[d.d.ke].activeMonitors[d.d.id]
|
||||
break;
|
||||
case'sync':
|
||||
s.initiateMonitorObject(d.sync);
|
||||
Object.keys(d.sync).forEach(function(v){
|
||||
s.group[d.sync.ke].mon[d.sync.mid][v]=d.sync[v];
|
||||
s.group[d.sync.ke].activeMonitors[d.sync.mid][v]=d.sync[v];
|
||||
});
|
||||
break;
|
||||
case'delete'://delete video
|
||||
s.file('delete',s.dir.videos+d.ke+'/'+d.mid+'/'+d.file)
|
||||
break;
|
||||
case'deleteTimelapseFrame'://delete video
|
||||
var filePath = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/` + d.file
|
||||
s.file('delete',filePath)
|
||||
break;
|
||||
case'insertCompleted'://close video
|
||||
s.insertCompletedVideo(d.d,d.k)
|
||||
break;
|
||||
|
@ -212,6 +270,15 @@ module.exports = function(s,config,lang,app,io){
|
|||
})
|
||||
childIO.on('disconnect',function(d){
|
||||
s.connected = false;
|
||||
var groupKeys = Object.keys(s.group)
|
||||
groupKeys.forEach(function(groupKey){
|
||||
var activeMonitorKeys = Object.keys(s.group[groupKey].activeMonitors)
|
||||
activeMonitorKeys.forEach(function(monitorKey){
|
||||
var activeMonitor = s.group[groupKey].activeMonitors[monitorKey]
|
||||
if(activeMonitor && activeMonitor.spawn && activeMonitor.spawn.close)activeMonitor.spawn.close()
|
||||
if(activeMonitor && activeMonitor.spawn && activeMonitor.spawn.kill)activeMonitor.spawn.kill()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ module.exports = function(s){
|
|||
}
|
||||
var config = require(s.location.config);
|
||||
if(!config.productType){
|
||||
config.productType='CE'
|
||||
config.productType = 'CE'
|
||||
}
|
||||
//config defaults
|
||||
if(config.cpuUsageMarker === undefined){config.cpuUsageMarker='%Cpu'}
|
||||
if(config.customCpuCommand === undefined){config.customCpuCommand=null}
|
||||
if(config.autoDropCache === undefined){config.autoDropCache=true}
|
||||
if(config.autoDropCache === undefined){config.autoDropCache=false}
|
||||
if(config.doSnapshot === undefined){config.doSnapshot=true}
|
||||
if(config.restart === undefined){config.restart={}}
|
||||
if(config.systemLog === undefined){config.systemLog=true}
|
||||
|
@ -27,7 +27,8 @@ module.exports = function(s){
|
|||
if(config.cron.deleteOverMaxOffset === undefined)config.cron.deleteOverMaxOffset=0.9;
|
||||
if(config.cron.deleteLogs === undefined)config.cron.deleteLogs=true;
|
||||
if(config.cron.deleteEvents === undefined)config.cron.deleteEvents=true;
|
||||
if(config.cron.deleteFileBins === undefined)config.cron.deleteFileBins=true;
|
||||
if(config.cron.deleteFileBinsOverMax === undefined)config.cron.deleteFileBins=true;
|
||||
if(config.deleteFileBins === undefined)config.deleteFileBinsOverMax=true;
|
||||
if(config.cron.interval === undefined)config.cron.interval=1;
|
||||
if(config.databaseType === undefined){config.databaseType='mysql'}
|
||||
if(config.pluginKeys === undefined)config.pluginKeys={};
|
||||
|
@ -37,7 +38,7 @@ module.exports = function(s){
|
|||
if(config.pipeAddition === undefined){config.pipeAddition=10}else{config.pipeAddition=parseInt(config.pipeAddition)}
|
||||
if(config.hideCloudSaveUrls === undefined){config.hideCloudSaveUrls = true}
|
||||
if(config.insertOrphans === undefined){config.insertOrphans = true}
|
||||
if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 20}
|
||||
if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 2}
|
||||
if(config.detectorMergePamRegionTriggers === undefined){config.detectorMergePamRegionTriggers = false}
|
||||
//Child Nodes
|
||||
if(config.childNodes === undefined)config.childNodes = {};
|
||||
|
|
|
@ -1,6 +1,28 @@
|
|||
var fs = require('fs')
|
||||
var express = require('express')
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
function mergeDeep(...objects) {
|
||||
const isObject = obj => obj && typeof obj === 'object';
|
||||
|
||||
return objects.reduce((prev, obj) => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const pVal = prev[key];
|
||||
const oVal = obj[key];
|
||||
|
||||
if (Array.isArray(pVal) && Array.isArray(oVal)) {
|
||||
prev[key] = pVal.concat(...oVal);
|
||||
}
|
||||
else if (isObject(pVal) && isObject(oVal)) {
|
||||
prev[key] = mergeDeep(pVal, oVal);
|
||||
}
|
||||
else {
|
||||
prev[key] = oVal;
|
||||
}
|
||||
});
|
||||
|
||||
return prev;
|
||||
}, {});
|
||||
}
|
||||
s.customAutoLoadModules = {}
|
||||
s.customAutoLoadTree = {
|
||||
pages: [],
|
||||
|
@ -14,7 +36,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
superLibsJs: [],
|
||||
superLibsCss: []
|
||||
}
|
||||
var folderPath = __dirname + '/customAutoLoad'
|
||||
var folderPath = s.mainDirectory + '/libs/customAutoLoad'
|
||||
var search = function(searchFor,searchIn){return searchIn.indexOf(searchFor) > -1}
|
||||
fs.readdir(folderPath,function(err,folderContents){
|
||||
if(!err && folderContents){
|
||||
|
@ -117,6 +139,24 @@ module.exports = function(s,config,lang,app,io){
|
|||
})
|
||||
})
|
||||
break;
|
||||
case'definitions':
|
||||
var definitionsFolder = s.checkCorrectPathEnding(customModulePath) + 'definitions/'
|
||||
fs.readdir(definitionsFolder,function(err,files){
|
||||
if(err)return console.log(err);
|
||||
files.forEach(function(filename){
|
||||
var fileData = require(definitionsFolder + filename)
|
||||
var rule = filename.replace('.json','').replace('.js','')
|
||||
if(config.language === rule){
|
||||
s.definitions = mergeDeep(s.definitions,fileData)
|
||||
}
|
||||
if(s.loadedDefinitons[rule]){
|
||||
s.loadedDefinitons[rule] = mergeDeep(s.loadedDefinitons[rule],fileData)
|
||||
}else{
|
||||
s.loadedDefinitons[rule] = mergeDeep(s.copySystemDefaultDefinitions(),fileData)
|
||||
}
|
||||
})
|
||||
})
|
||||
break;
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
44
libs/definitions.js
Normal file
44
libs/definitions.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
var fs = require('fs')
|
||||
var express = require('express')
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
s.location.definitions = s.mainDirectory+'/definitions'
|
||||
try{
|
||||
var definitions = require(s.location.definitions+'/'+config.language+'.js')(s,config,lang)
|
||||
}catch(er){
|
||||
console.error(er)
|
||||
console.log('There was an error loading your definition file.')
|
||||
try{
|
||||
var definitions = require(s.location.definitions+'/en_CA.js')(s,config,lang)
|
||||
}catch(er){
|
||||
console.error(er)
|
||||
console.log('There was an error loading your definition file.')
|
||||
var definitions = require(s.location.definitions+'/en_CA.json');
|
||||
}
|
||||
}
|
||||
//load defintions dynamically
|
||||
s.definitions = definitions
|
||||
s.copySystemDefaultDefinitions = function(){
|
||||
//en_CA
|
||||
return Object.assign(s.definitions,{})
|
||||
}
|
||||
s.loadedDefinitons={}
|
||||
s.loadedDefinitons[config.language] = s.copySystemDefaultDefinitions()
|
||||
s.getDefinitonFile = function(rule){
|
||||
if(rule && rule !== ''){
|
||||
var file = s.loadedDefinitons[rule]
|
||||
if(!file){
|
||||
try{
|
||||
s.loadedDefinitons[rule] = require(s.location.definitions+'/'+rule+'.js')(s,config,lang)
|
||||
s.loadedDefinitons[rule] = Object.assign(s.copySystemDefaultDefinitions(),s.loadedDefinitons[rule])
|
||||
file = s.loadedDefinitons[rule]
|
||||
}catch(err){
|
||||
file = s.copySystemDefaultDefinitions()
|
||||
}
|
||||
}
|
||||
}else{
|
||||
file = s.copySystemDefaultDefinitions()
|
||||
}
|
||||
return file
|
||||
}
|
||||
return definitions
|
||||
}
|
|
@ -13,9 +13,9 @@ module.exports = function(s,config){
|
|||
globalSensitivity,
|
||||
globalColorThreshold,
|
||||
fullFrame = false
|
||||
if(s.group[e.ke].mon_conf[e.id].details.detector_scale_x===''||s.group[e.ke].mon_conf[e.id].details.detector_scale_y===''){
|
||||
width = s.group[e.ke].mon_conf[e.id].details.detector_scale_x;
|
||||
height = s.group[e.ke].mon_conf[e.id].details.detector_scale_y;
|
||||
if(s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x===''||s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y===''){
|
||||
width = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x;
|
||||
height = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y;
|
||||
}else{
|
||||
width = e.width
|
||||
height = e.height
|
||||
|
@ -35,9 +35,9 @@ module.exports = function(s,config){
|
|||
|
||||
var regionJson
|
||||
try{
|
||||
regionJson = JSON.parse(s.group[e.ke].mon_conf[e.id].details.cords)
|
||||
regionJson = JSON.parse(s.group[e.ke].rawMonitorConfigurations[e.id].details.cords)
|
||||
}catch(err){
|
||||
regionJson = s.group[e.ke].mon_conf[e.id].details.cords
|
||||
regionJson = s.group[e.ke].rawMonitorConfigurations[e.id].details.cords
|
||||
}
|
||||
|
||||
if(Object.keys(regionJson).length === 0 || e.details.detector_frame === '1'){
|
||||
|
@ -64,8 +64,8 @@ module.exports = function(s,config){
|
|||
if(e.details.detector_show_matrix==='1'){
|
||||
pamDiffOptions.response = 'bounds'
|
||||
}
|
||||
s.group[e.ke].mon[e.id].pamDiff = new PamDiff(pamDiffOptions);
|
||||
s.group[e.ke].mon[e.id].p2p = new P2P()
|
||||
s.group[e.ke].activeMonitors[e.id].pamDiff = new PamDiff(pamDiffOptions);
|
||||
s.group[e.ke].activeMonitors[e.id].p2p = new P2P()
|
||||
var regionArray = Object.values(regionJson)
|
||||
if(config.detectorMergePamRegionTriggers === true){
|
||||
// merge pam triggers for performance boost
|
||||
|
@ -116,12 +116,12 @@ module.exports = function(s,config){
|
|||
}
|
||||
}
|
||||
if(e.details.detector_noise_filter==='1'){
|
||||
if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {}
|
||||
var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray
|
||||
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
|
||||
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
|
||||
Object.keys(regions.notForPam).forEach(function(name){
|
||||
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
|
||||
})
|
||||
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
|
||||
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
||||
var filteredCount = 0
|
||||
var filteredCountSuccess = 0
|
||||
data.trigger.forEach(function(trigger){
|
||||
|
@ -135,7 +135,7 @@ module.exports = function(s,config){
|
|||
})
|
||||
})
|
||||
}else{
|
||||
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
|
||||
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
||||
buildTriggerEvent(s.mergePamTriggers(data))
|
||||
})
|
||||
}
|
||||
|
@ -170,12 +170,12 @@ module.exports = function(s,config){
|
|||
})
|
||||
}
|
||||
if(e.details.detector_noise_filter==='1'){
|
||||
if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {}
|
||||
var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray
|
||||
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
|
||||
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
|
||||
Object.keys(regions.notForPam).forEach(function(name){
|
||||
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
|
||||
})
|
||||
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
|
||||
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
||||
data.trigger.forEach(function(trigger){
|
||||
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
|
||||
s.createMatrixFromPamTrigger(trigger)
|
||||
|
@ -184,7 +184,7 @@ module.exports = function(s,config){
|
|||
})
|
||||
})
|
||||
}else{
|
||||
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
|
||||
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
||||
data.trigger.forEach(function(trigger){
|
||||
s.createMatrixFromPamTrigger(trigger)
|
||||
buildTriggerEvent(trigger)
|
||||
|
@ -210,7 +210,14 @@ module.exports = function(s,config){
|
|||
if(!region)return false;
|
||||
region.polygon = [];
|
||||
region.points.forEach(function(points){
|
||||
region.polygon.push({x:parseFloat(points[0]),y:parseFloat(points[1])})
|
||||
var x = parseFloat(points[0]);
|
||||
var y = parseFloat(points[1]);
|
||||
if(x < 0)x = 0;
|
||||
if(y < 0)y = 0;
|
||||
region.polygon.push({
|
||||
x: x,
|
||||
y: y
|
||||
})
|
||||
})
|
||||
if(region.sensitivity===''){
|
||||
region.sensitivity = globalSensitivity
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
var fs = require('fs')
|
||||
var execSync = require('child_process').execSync
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
if(config.dropInEventServer === true){
|
||||
if(config.dropInEventForceSaveEvent === undefined)config.dropInEventForceSaveEvent = true
|
||||
if(config.dropInEventDeleteFileAfterTrigger === undefined)config.dropInEventDeleteFileAfterTrigger = true
|
||||
var authenticateUser = function(username,password,callback){
|
||||
var splitUsername = username.split('@')
|
||||
if(splitUsername[1] !== 'Shinobi' && splitUsername[1] !== 'shinobi'){
|
||||
s.sqlQuery('SELECT ke,uid FROM Users WHERE mail=? AND (pass=? OR pass=?)',[
|
||||
username,
|
||||
password,
|
||||
s.createHash(password)
|
||||
],function(err,r){
|
||||
var user
|
||||
if(r && r[0]){
|
||||
user = r[0]
|
||||
}
|
||||
callback(err,user)
|
||||
})
|
||||
}else{
|
||||
s.sqlQuery('SELECT ke,uid FROM API WHERE code=? AND ke=?',[
|
||||
splitUsername[0], //code
|
||||
password //ke
|
||||
],function(err,r){
|
||||
var apiKey
|
||||
if(r && r[0]){
|
||||
apiKey = r[0]
|
||||
}
|
||||
callback(err,apiKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
var beforeMonitorsLoadedOnStartup = function(){
|
||||
if(!config.dropInEventsDir){
|
||||
config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
|
||||
|
@ -50,74 +23,131 @@ module.exports = function(s,config,lang,app,io){
|
|||
var onMonitorStop = function(monitorConfig){
|
||||
var ke = monitorConfig.ke
|
||||
var mid = monitorConfig.mid
|
||||
if(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher){
|
||||
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher.close()
|
||||
delete(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher)
|
||||
if(s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher){
|
||||
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher.close()
|
||||
delete(s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher)
|
||||
var monitorEventDropDir = getDropInEventDir(monitorConfig)
|
||||
s.file('deleteFolder',monitorEventDropDir + '*')
|
||||
}
|
||||
var monitorEventDropDir = getDropInEventDir(monitorConfig)
|
||||
if(fs.existsSync(monitorEventDropDir))execSync('rm -rf ' + monitorEventDropDir)
|
||||
}
|
||||
var createDropInEventDirectory = function(e,callback){
|
||||
var directory = s.dir.dropInEvents + e.ke + '/'
|
||||
fs.mkdir(directory,function(err){
|
||||
s.handleFolderError(err)
|
||||
directory = s.dir.dropInEvents + e.ke + '/' + (e.id || e.mid) + '/'
|
||||
fs.mkdir(directory,function(err){
|
||||
s.handleFolderError(err)
|
||||
callback(err,directory)
|
||||
})
|
||||
})
|
||||
}
|
||||
var onMonitorInit = function(monitorConfig){
|
||||
onMonitorStop(monitorConfig)
|
||||
var ke = monitorConfig.ke
|
||||
var mid = monitorConfig.mid
|
||||
var monitorEventDropDir = getDropInEventDir(monitorConfig)
|
||||
var groupEventDropDir = s.dir.dropInEvents + ke
|
||||
if(!fs.existsSync(groupEventDropDir)){
|
||||
fs.mkdirSync(groupEventDropDir)
|
||||
}
|
||||
var monitorEventDropDir = groupEventDropDir + '/' + mid + '/'
|
||||
if(!fs.existsSync(monitorEventDropDir)){
|
||||
fs.mkdirSync(monitorEventDropDir)
|
||||
}
|
||||
var fileQueue = {}
|
||||
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventFileQueue = fileQueue
|
||||
var eventTrigger = function(eventType,filename){
|
||||
var filePath = monitorEventDropDir + filename
|
||||
if(filename.indexOf('.jpg') > -1 || filename.indexOf('.jpeg') > -1){
|
||||
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
|
||||
fs.unlink(snapPath,function(err){
|
||||
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
|
||||
s.triggerEvent({
|
||||
id: mid,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: filename,
|
||||
plug: "dropInEvent",
|
||||
reason: "dropInEvent"
|
||||
createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){
|
||||
var monitorEventDropDir = getDropInEventDir(monitorConfig)
|
||||
var fileQueue = {}
|
||||
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventFileQueue = fileQueue
|
||||
var search = function(searchIn,searchFor){
|
||||
return searchIn.indexOf(searchFor) > -1
|
||||
}
|
||||
var processFile = function(filename){
|
||||
var filePath = monitorEventDropDir + filename
|
||||
if(search(filename,'.jpg') || search(filename,'.jpeg')){
|
||||
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
|
||||
fs.unlink(snapPath,function(err){
|
||||
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
|
||||
s.triggerEvent({
|
||||
id: mid,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: filename,
|
||||
plug: "dropInEvent",
|
||||
reason: "ftpServer"
|
||||
},
|
||||
},config.dropInEventForceSaveEvent)
|
||||
})
|
||||
}else{
|
||||
var reason = "ftpServer"
|
||||
if(search(filename,'.mp4')){
|
||||
fs.stat(filePath,function(err,stats){
|
||||
var startTime = stats.ctime
|
||||
var endTime = stats.mtime
|
||||
var shinobiFilename = s.formattedTime(startTime) + '.mp4'
|
||||
var recordingPath = s.getVideoDirectory(monitorConfig) + shinobiFilename
|
||||
var writeStream = fs.createWriteStream(recordingPath)
|
||||
fs.createReadStream(filePath).pipe(writeStream)
|
||||
writeStream.on('finish', () => {
|
||||
s.insertCompletedVideo(s.group[monitorConfig.ke].rawMonitorConfigurations[monitorConfig.mid],{
|
||||
file : shinobiFilename
|
||||
},function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
var completeAction = function(){
|
||||
s.triggerEvent({
|
||||
id: mid,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: filename,
|
||||
plug: "dropInEvent",
|
||||
reason: reason
|
||||
}
|
||||
},config.dropInEventForceSaveEvent)
|
||||
}
|
||||
if(search(filename,'.txt')){
|
||||
fs.readFile(filePath,{encoding: 'utf-8'},function(err,data){
|
||||
if(data){
|
||||
reason = data.split('\n')[0] || filename
|
||||
}else if(filename){
|
||||
reason = filename
|
||||
}
|
||||
completeAction()
|
||||
})
|
||||
}else{
|
||||
completeAction()
|
||||
}
|
||||
|
||||
}
|
||||
if(config.dropInEventDeleteFileAfterTrigger){
|
||||
setTimeout(function(){
|
||||
fs.unlink(filePath,function(err){
|
||||
|
||||
})
|
||||
},1000 * 60 * 5)
|
||||
}
|
||||
}
|
||||
var eventTrigger = function(eventType,filename,stats){
|
||||
if(stats.isDirectory()){
|
||||
fs.readdir(monitorEventDropDir + filename,function(err,files){
|
||||
if(files){
|
||||
files.forEach(function(filename){
|
||||
processFile(filename)
|
||||
})
|
||||
}else if(err){
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}else{
|
||||
s.triggerEvent({
|
||||
id: mid,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: filename,
|
||||
plug: "dropInEvent",
|
||||
reason: "ftpServer"
|
||||
}else{
|
||||
processFile(filename)
|
||||
}
|
||||
}
|
||||
var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){
|
||||
fs.stat(monitorEventDropDir + filename,function(err,stats){
|
||||
if(!err){
|
||||
clearTimeout(fileQueue[filename])
|
||||
fileQueue[filename] = setTimeout(function(){
|
||||
eventTrigger(eventType,filename,stats)
|
||||
},1750)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(config.dropInEventDeleteFileAfterTrigger){
|
||||
setTimeout(function(){
|
||||
fs.unlink(filePath,function(err){
|
||||
|
||||
})
|
||||
},1000 * 60 * 5)
|
||||
}
|
||||
}
|
||||
var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){
|
||||
if(fs.existsSync(monitorEventDropDir + filename)){
|
||||
clearTimeout(fileQueue[filename])
|
||||
fileQueue[filename] = setTimeout(function(){
|
||||
eventTrigger(eventType,filename)
|
||||
},1200)
|
||||
}
|
||||
})
|
||||
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher = directoryWatch
|
||||
})
|
||||
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher = directoryWatch
|
||||
}
|
||||
// FTP Server
|
||||
if(config.ftpServer === true){
|
||||
|
@ -127,13 +157,12 @@ module.exports = function(s,config,lang,app,io){
|
|||
const FtpSrv = require('ftp-srv')
|
||||
const ftpServer = new FtpSrv({
|
||||
url: config.ftpServerUrl,
|
||||
// log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){}
|
||||
})
|
||||
|
||||
ftpServer.on('login', (data, resolve, reject) => {
|
||||
var username = data.username
|
||||
var password = data.password
|
||||
authenticateUser(username,password,function(err,user){
|
||||
s.basicOrApiAuthentication(username,password,function(err,user){
|
||||
if(user){
|
||||
resolve({root: s.dir.dropInEvents + user.ke})
|
||||
}else{
|
||||
|
@ -141,7 +170,9 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
ftpServer.on('client-error', ({connection, context, error}) => {
|
||||
console.log('error')
|
||||
})
|
||||
ftpServer.listen().then(() => {
|
||||
s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`)
|
||||
}).catch(function(err){
|
||||
|
@ -156,13 +187,15 @@ module.exports = function(s,config,lang,app,io){
|
|||
// SMTP Server
|
||||
// allow starting SMTP server without dropInEventServer
|
||||
if(config.smtpServer === true){
|
||||
if(config.smtpServerHideStartTls === undefined)config.smtpServerHideStartTls = null
|
||||
var SMTPServer = require("smtp-server").SMTPServer;
|
||||
if(!config.smtpServerPort && (config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl)){config.smtpServerPort = 465}else if(!config.smtpServerPort){config.smtpServerPort = 25}
|
||||
var smtpOptions = {
|
||||
hideSTARTTLS: config.smtpServerHideStartTls,
|
||||
onAuth(auth, session, callback) {
|
||||
var username = auth.username
|
||||
var password = auth.password
|
||||
authenticateUser(username,password,function(err,user){
|
||||
s.basicOrApiAuthentication(username,password,function(err,user){
|
||||
if(user){
|
||||
callback(null, {user: user.ke})
|
||||
}else{
|
||||
|
@ -174,21 +207,61 @@ module.exports = function(s,config,lang,app,io){
|
|||
var split = address.address.split('@')
|
||||
var monitorId = split[0]
|
||||
var ke = session.user
|
||||
if(s.group[ke].mon_conf[monitorId] && s.group[ke].mon[monitorId].isStarted === true){
|
||||
s.triggerEvent({
|
||||
id: monitorId,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: address.address,
|
||||
plug: "dropInEvent",
|
||||
reason: "smtpServer"
|
||||
}
|
||||
})
|
||||
if(s.group[ke].rawMonitorConfigurations[monitorId] && s.group[ke].activeMonitors[monitorId].isStarted === true){
|
||||
session.monitorId = monitorId
|
||||
}else{
|
||||
return callback(new Error(lang['No Monitor Exists with this ID.']))
|
||||
}
|
||||
callback()
|
||||
},
|
||||
onData(stream, session, callback) {
|
||||
if(session.monitorId){
|
||||
var ke = session.user
|
||||
var monitorId = session.monitorId
|
||||
var reasonTag = 'smtpServer'
|
||||
var text = ''
|
||||
stream.on('data',function(data){
|
||||
text += data.toString()
|
||||
}) // print message to console
|
||||
stream.on("end", function(){
|
||||
var contentPart = text.split('--PartBoundary12345678')
|
||||
contentPart.forEach(function(part){
|
||||
var parsed = {}
|
||||
var lines = part.split(/\r?\n/)
|
||||
lines.forEach(function(line,n){
|
||||
var pieces = line.split(':')
|
||||
if(pieces[1]){
|
||||
var nextLine = lines[n + 1]
|
||||
var keyName = pieces[0].trim().toLowerCase()
|
||||
pieces.shift()
|
||||
var parsedValue = pieces.join(':')
|
||||
parsed[keyName] = parsedValue
|
||||
}
|
||||
})
|
||||
if(parsed['content-type'] && parsed['content-type'].indexOf('image/jpeg') > -1){
|
||||
// console.log(lines)
|
||||
}
|
||||
if(parsed['alarm event']){
|
||||
reasonTag = parsed['alarm event']
|
||||
}else if(parsed.subject){
|
||||
reasonTag = parsed.subject
|
||||
}
|
||||
})
|
||||
s.triggerEvent({
|
||||
id: monitorId,
|
||||
ke: ke,
|
||||
details: {
|
||||
confidence: 100,
|
||||
name: 'smtpServer',
|
||||
plug: "dropInEvent",
|
||||
reason: reasonTag
|
||||
}
|
||||
},config.dropInEventForceSaveEvent)
|
||||
callback()
|
||||
})
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
if(config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl && config.ssl.cert && config.ssl.key){
|
||||
|
|
111
libs/events.js
111
libs/events.js
|
@ -41,7 +41,7 @@ module.exports = function(s,config,lang){
|
|||
extender(x,d)
|
||||
})
|
||||
}
|
||||
s.triggerEvent = function(d){
|
||||
s.triggerEvent = function(d,forceSave){
|
||||
var filter = {
|
||||
halt : false,
|
||||
addToMotionCounter : true,
|
||||
|
@ -56,11 +56,11 @@ module.exports = function(s,config,lang){
|
|||
extender(d,filter)
|
||||
})
|
||||
var detailString = JSON.stringify(d.details);
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]){
|
||||
return s.systemLog(lang['No Monitor Found, Ignoring Request'])
|
||||
}
|
||||
d.mon=s.group[d.ke].mon_conf[d.id];
|
||||
var currentConfig = s.group[d.ke].mon[d.id].details
|
||||
d.mon=s.group[d.ke].rawMonitorConfigurations[d.id];
|
||||
var currentConfig = s.group[d.ke].activeMonitors[d.id].details
|
||||
var hasMatrices = (d.details.matrices && d.details.matrices.length > 0)
|
||||
//read filters
|
||||
if(
|
||||
|
@ -185,15 +185,16 @@ module.exports = function(s,config,lang){
|
|||
d.details.matrices = reviewedMatrix
|
||||
}
|
||||
}
|
||||
var eventTime = new Date()
|
||||
//motion counter
|
||||
if(filter.addToMotionCounter && filter.record){
|
||||
if(!s.group[d.ke].mon[d.id].detector_motion_count){
|
||||
s.group[d.ke].mon[d.id].detector_motion_count=0
|
||||
if(!s.group[d.ke].activeMonitors[d.id].detector_motion_count){
|
||||
s.group[d.ke].activeMonitors[d.id].detector_motion_count=0
|
||||
}
|
||||
s.group[d.ke].mon[d.id].detector_motion_count+=1
|
||||
s.group[d.ke].activeMonitors[d.id].detector_motion_count+=1
|
||||
}
|
||||
if(filter.useLock){
|
||||
if(s.group[d.ke].mon[d.id].motion_lock){
|
||||
if(s.group[d.ke].activeMonitors[d.id].motion_lock){
|
||||
return
|
||||
}
|
||||
var detector_lock_timeout
|
||||
|
@ -201,10 +202,10 @@ module.exports = function(s,config,lang){
|
|||
detector_lock_timeout = 2000
|
||||
}
|
||||
detector_lock_timeout = parseFloat(currentConfig.detector_lock_timeout);
|
||||
if(!s.group[d.ke].mon[d.id].detector_lock_timeout){
|
||||
s.group[d.ke].mon[d.id].detector_lock_timeout=setTimeout(function(){
|
||||
clearTimeout(s.group[d.ke].mon[d.id].detector_lock_timeout)
|
||||
delete(s.group[d.ke].mon[d.id].detector_lock_timeout)
|
||||
if(!s.group[d.ke].activeMonitors[d.id].detector_lock_timeout){
|
||||
s.group[d.ke].activeMonitors[d.id].detector_lock_timeout=setTimeout(function(){
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
|
||||
delete(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
|
||||
},detector_lock_timeout)
|
||||
}else{
|
||||
return
|
||||
|
@ -212,7 +213,7 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
// check if object should be in region
|
||||
if(hasMatrices && currentConfig.detector_obj_region === '1'){
|
||||
var regions = s.group[d.ke].mon[d.id].parsedObjects.cords
|
||||
var regions = s.group[d.ke].activeMonitors[d.id].parsedObjects.cords
|
||||
var isMatrixInRegions = s.isAtleastOneMatrixInRegion(regions,d.details.matrices)
|
||||
if(isMatrixInRegions){
|
||||
s.debugLog('Matrix in region!')
|
||||
|
@ -229,14 +230,14 @@ module.exports = function(s,config,lang){
|
|||
if(d.doObjectDetection === true){
|
||||
s.ocvTx({
|
||||
f : 'frame',
|
||||
mon : s.group[d.ke].mon_conf[d.id].details,
|
||||
mon : s.group[d.ke].rawMonitorConfigurations[d.id].details,
|
||||
ke : d.ke,
|
||||
id : d.id,
|
||||
time : s.formattedTime(),
|
||||
frame : s.group[d.ke].mon[d.id].lastJpegDetectorFrame
|
||||
frame : s.group[d.ke].activeMonitors[d.id].lastJpegDetectorFrame
|
||||
})
|
||||
}else{
|
||||
if(currentConfig.detector_multi_trigger === '1'){
|
||||
if(currentConfig.det_multi_trig === '1'){
|
||||
s.getCamerasForMultiTrigger(d.mon).forEach(function(monitor){
|
||||
if(monitor.mid !== d.id){
|
||||
s.triggerEvent({
|
||||
|
@ -253,8 +254,8 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
}
|
||||
//save this detection result in SQL, only coords. not image.
|
||||
if(filter.save && currentConfig.detector_save === '1'){
|
||||
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,new Date()])
|
||||
if(forceSave || (filter.save && currentConfig.detector_save === '1')){
|
||||
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,eventTime])
|
||||
}
|
||||
if(currentConfig.detector_notrigger === '1'){
|
||||
var detector_notrigger_timeout
|
||||
|
@ -262,9 +263,9 @@ module.exports = function(s,config,lang){
|
|||
detector_notrigger_timeout = 10
|
||||
}
|
||||
detector_notrigger_timeout = parseFloat(currentConfig.detector_notrigger_timeout)*1000*60;
|
||||
s.group[d.ke].mon[d.id].detector_notrigger_timeout = detector_notrigger_timeout;
|
||||
clearInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout)
|
||||
s.group[d.ke].mon[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
|
||||
s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout = detector_notrigger_timeout;
|
||||
clearInterval(s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout)
|
||||
s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
|
||||
}
|
||||
var detector_timeout
|
||||
if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
|
||||
|
@ -273,7 +274,7 @@ module.exports = function(s,config,lang){
|
|||
detector_timeout = parseFloat(currentConfig.detector_timeout)
|
||||
}
|
||||
if(filter.record && d.mon.mode=='start'&¤tConfig.detector_trigger==='1'&¤tConfig.detector_record_method==='sip'){
|
||||
s.createEventBasedRecording(d)
|
||||
s.createEventBasedRecording(d,moment(eventTime).subtract(5,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
|
||||
}else if(filter.record && d.mon.mode!=='stop'&¤tConfig.detector_trigger=='1'&¤tConfig.detector_record_method==='hot'){
|
||||
if(!d.auth){
|
||||
d.auth=s.gid();
|
||||
|
@ -314,26 +315,32 @@ module.exports = function(s,config,lang){
|
|||
|
||||
if(filter.webhook && currentConfig.detector_webhook === '1'){
|
||||
var detector_webhook_url = addEventDetailsToString(d,currentConfig.detector_webhook_url)
|
||||
request({url:detector_webhook_url,method:'GET',encoding:null},function(err,data){
|
||||
var webhookMethod = currentConfig.detector_webhook_method
|
||||
if(!webhookMethod || webhookMethod === '')webhookMethod = 'GET'
|
||||
request(detector_webhook_url,{method: webhookMethod,encoding:null},function(err,data){
|
||||
if(err){
|
||||
s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].mon[d.id].detector_command){
|
||||
s.createTimeout(s.group[d.ke].mon[d.id].detector_command,currentConfig.detector_command_timeout,10)
|
||||
if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].activeMonitors[d.id].detector_command){
|
||||
s.group[d.ke].activeMonitors[d.id].detector_command = s.createTimeout('detector_command',s.group[d.ke].activeMonitors[d.id],currentConfig.detector_command_timeout,10)
|
||||
var detector_command = addEventDetailsToString(d,currentConfig.detector_command)
|
||||
exec(detector_command,{detached: true})
|
||||
if(detector_command === '')return
|
||||
exec(detector_command,{detached: true},function(err){
|
||||
if(err)s.debugLog(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
//show client machines the event
|
||||
d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
|
||||
s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
|
||||
}
|
||||
s.createEventBasedRecording = function(d){
|
||||
d.mon = s.group[d.ke].mon_conf[d.id]
|
||||
var currentConfig = s.group[d.ke].mon[d.id].details
|
||||
s.createEventBasedRecording = function(d,fileTime){
|
||||
if(!fileTime)fileTime = s.formattedTime()
|
||||
d.mon = s.group[d.ke].rawMonitorConfigurations[d.id]
|
||||
var currentConfig = s.group[d.ke].activeMonitors[d.id].details
|
||||
if(currentConfig.detector !== '1'){
|
||||
return
|
||||
}
|
||||
|
@ -343,29 +350,29 @@ module.exports = function(s,config,lang){
|
|||
}else{
|
||||
detector_timeout = parseFloat(currentConfig.detector_timeout)
|
||||
}
|
||||
if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].mon[d.id].eventBasedRecording.timeout){
|
||||
clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.timeout = setTimeout(function(){
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = true
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.process.stdin.setEncoding('utf8')
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.process.stdin.write('q')
|
||||
delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
|
||||
if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout){
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout = setTimeout(function(){
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = true
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stdin.setEncoding('utf8')
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stdin.write('q')
|
||||
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
|
||||
},detector_timeout * 1000 * 60)
|
||||
}
|
||||
if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false;
|
||||
if(!s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process){
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = false;
|
||||
var runRecord = function(){
|
||||
var filename = s.formattedTime()+'.mp4'
|
||||
var filename = fileTime+'.mp4'
|
||||
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Started"]})
|
||||
//-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))
|
||||
var ffmpegError='';
|
||||
var error
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.process.stderr.on('data',function(data){
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stderr.on('data',function(data){
|
||||
s.userLog(d,{type:lang["Traditional Recording"],msg:data.toString()})
|
||||
})
|
||||
s.group[d.ke].mon[d.id].eventBasedRecording.process.on('close',function(){
|
||||
if(!s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd){
|
||||
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.on('close',function(){
|
||||
if(!s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd){
|
||||
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Process Exited Prematurely. Restarting."]})
|
||||
runRecord()
|
||||
return
|
||||
|
@ -375,22 +382,22 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Complete"]})
|
||||
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Clear Recorder Process"]})
|
||||
delete(s.group[d.ke].mon[d.id].eventBasedRecording.process)
|
||||
clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
|
||||
delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
|
||||
clearTimeout(s.group[d.ke].mon[d.id].recordingChecker)
|
||||
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process)
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
|
||||
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].recordingChecker)
|
||||
})
|
||||
}
|
||||
runRecord()
|
||||
}
|
||||
}
|
||||
s.closeEventBasedRecording = function(e){
|
||||
if(s.group[e.ke].mon[e.id].eventBasedRecording.process){
|
||||
clearTimeout(s.group[e.ke].mon[e.id].eventBasedRecording.timeout)
|
||||
s.group[e.ke].mon[e.id].eventBasedRecording.allowEnd = true;
|
||||
s.group[e.ke].mon[e.id].eventBasedRecording.process.kill('SIGTERM');
|
||||
if(s.group[e.ke].activeMonitors[e.id].eventBasedRecording.process){
|
||||
clearTimeout(s.group[e.ke].activeMonitors[e.id].eventBasedRecording.timeout)
|
||||
s.group[e.ke].activeMonitors[e.id].eventBasedRecording.allowEnd = true;
|
||||
s.group[e.ke].activeMonitors[e.id].eventBasedRecording.process.kill('SIGTERM');
|
||||
}
|
||||
// var stackedProcesses = s.group[e.ke].mon[e.id].eventBasedRecording.stackable
|
||||
// var stackedProcesses = s.group[e.ke].activeMonitors[e.id].eventBasedRecording.stackable
|
||||
// Object.keys(stackedProcesses).forEach(function(key){
|
||||
// var item = stackedProcesses[key]
|
||||
// clearTimeout(item.timeout)
|
||||
|
|
|
@ -143,4 +143,28 @@ module.exports = function(s,config){
|
|||
s.onWebSocketDisconnectionExtensions.push(callback)
|
||||
}
|
||||
//
|
||||
s.onGetCpuUsageExtensions = []
|
||||
s.onGetCpuUsage = function(callback){
|
||||
s.onGetCpuUsageExtensions.push(callback)
|
||||
}
|
||||
//
|
||||
s.onGetRamUsageExtensions = []
|
||||
s.onGetRamUsage = function(callback){
|
||||
s.onGetRamUsageExtensions.push(callback)
|
||||
}
|
||||
//
|
||||
/////// VIDEOS ////////
|
||||
s.insertCompletedVideoExtensions = []
|
||||
s.insertCompletedVideoExtender = function(callback){
|
||||
s.insertCompletedVideoExtensions.push(callback)
|
||||
}
|
||||
s.onBeforeInsertCompletedVideoExtensions = []
|
||||
s.onBeforeInsertCompletedVideo = function(callback){
|
||||
s.onBeforeInsertCompletedVideoExtensions.push(callback)
|
||||
}
|
||||
/////// TIMELAPSE ////////
|
||||
s.onInsertTimelapseFrameExtensions = []
|
||||
s.onInsertTimelapseFrame = function(callback){
|
||||
s.onInsertTimelapseFrameExtensions.push(callback)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
var fs = require('fs');
|
||||
var spawn = require('child_process').spawn;
|
||||
var execSync = require('child_process').execSync;
|
||||
module.exports = function(s,config,onFinish){
|
||||
module.exports = function(s,config,lang,onFinish){
|
||||
if(config.ffmpegBinary)config.ffmpegDir = config.ffmpegBinary
|
||||
var ffmpeg = {}
|
||||
var downloadingFfmpeg = false;
|
||||
//check local ffmpeg
|
||||
|
@ -98,6 +99,27 @@ module.exports = function(s,config,onFinish){
|
|||
config.availableHWAccels = availableHWAccels
|
||||
config.availableHWAccels = ['auto'].concat(config.availableHWAccels)
|
||||
console.log('Available Hardware Acceleration Methods : ',availableHWAccels.join(', '))
|
||||
var methods = {
|
||||
auto: {label:lang['Auto'],value:'auto'},
|
||||
drm: {label:lang['drm'],value:'drm'},
|
||||
cuvid: {label:lang['cuvid'],value:'cuvid'},
|
||||
vaapi: {label:lang['vaapi'],value:'vaapi'},
|
||||
qsv: {label:lang['qsv'],value:'qsv'},
|
||||
vdpau: {label:lang['vdpau'],value:'vdpau'},
|
||||
dxva2: {label:lang['dxva2'],value:'dxva2'},
|
||||
vdpau: {label:lang['vdpau'],value:'vdpau'},
|
||||
videotoolbox: {label:lang['videotoolbox'],value:'videotoolbox'}
|
||||
}
|
||||
s.listOfHwAccels = []
|
||||
config.availableHWAccels.forEach(function(availibleMethod){
|
||||
if(methods[availibleMethod]){
|
||||
var method = methods[availibleMethod]
|
||||
s.listOfHwAccels.push({
|
||||
name: method.label,
|
||||
value: method.value,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
@ -471,8 +493,7 @@ module.exports = function(s,config,onFinish){
|
|||
if(e.details.stream_timestamp_box_color&&e.details.stream_timestamp_box_color!==''){x.stream_timestamp_box_color=e.details.stream_timestamp_box_color}else{x.stream_timestamp_box_color='0x00000000@1'}
|
||||
//text size
|
||||
if(e.details.stream_timestamp_font_size&&e.details.stream_timestamp_font_size!==''){x.stream_timestamp_font_size=e.details.stream_timestamp_font_size}else{x.stream_timestamp_font_size='10'}
|
||||
|
||||
x.stream_video_filters.push('drawtext=fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size);
|
||||
x.stream_video_filters.push('drawtext="fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size + '"');
|
||||
}
|
||||
//stream - watermark for -vf
|
||||
if(e.details.stream_watermark&&e.details.stream_watermark=="1"&&e.details.stream_watermark_location&&e.details.stream_watermark_location!==''){
|
||||
|
@ -540,6 +561,7 @@ module.exports = function(s,config,onFinish){
|
|||
//add input feed map
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.stream)
|
||||
}
|
||||
if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps = ''}
|
||||
if(x.stream_fps && (e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64')){
|
||||
x.cust_stream += x.stream_fps
|
||||
}
|
||||
|
@ -638,8 +660,8 @@ module.exports = function(s,config,onFinish){
|
|||
x.record_video_filters = []
|
||||
x.record_string = ''
|
||||
//record - resolution
|
||||
if(e.width!==''&&e.height!==''&&!isNaN(e.width)&&!isNaN(e.height)){
|
||||
x.record_dimensions=' -s '+e.width+'x'+e.height
|
||||
if(e.record_scale_x!==''&&e.record_scale_y!==''&&e.record_scale_x!=='0'&&e.record_scale_y!=='0'&&!isNaN(e.record_scale_x)&&!isNaN(e.record_scale_y)){
|
||||
x.record_dimensions=' -s '+e.record_scale_x+'x'+e.record_scale_y
|
||||
}else{
|
||||
x.record_dimensions=''
|
||||
}
|
||||
|
@ -647,12 +669,13 @@ module.exports = function(s,config,onFinish){
|
|||
x.dimensions = e.details.stream_scale_x+'x'+e.details.stream_scale_y;
|
||||
}
|
||||
//record - segmenting
|
||||
x.segment=' -f segment -segment_format_options movflags=faststart+frag_keyframe+empty_moov -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
|
||||
x.segment = ' -f segment -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
|
||||
//record - set defaults for extension, video quality
|
||||
switch(e.ext){
|
||||
case'mp4':
|
||||
x.vcodec='libx264';x.acodec='aac';
|
||||
if(e.details.crf&&e.details.crf!==''){x.vcodec+=' -crf '+e.details.crf}
|
||||
x.record_video_filters.push(`-segment_format_options movflags=faststart+frag_keyframe+empty_moov`)
|
||||
break;
|
||||
case'webm':
|
||||
x.acodec='libvorbis',x.vcodec='libvpx';
|
||||
|
@ -680,8 +703,6 @@ module.exports = function(s,config,onFinish){
|
|||
if(x.vcodec.indexOf('none')>-1){x.vcodec=''}else{x.vcodec=' -vcodec '+x.vcodec}
|
||||
//record - frames per second (fps)
|
||||
if(e.fps&&e.fps!==''&&e.details.vcodec!=='copy'){x.record_fps=' -r '+e.fps}else{x.record_fps=''}
|
||||
//stream - frames per second (fps)
|
||||
if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps = ''}
|
||||
//record - timestamp options for -vf
|
||||
if(e.details.timestamp&&e.details.timestamp=="1"&&e.details.vcodec!=='copy'){
|
||||
//font
|
||||
|
@ -792,9 +813,11 @@ module.exports = function(s,config,onFinish){
|
|||
if(sendFramesGlobally)x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe pipe:3'
|
||||
if(e.details.detector_use_detect_object === '1'){
|
||||
//for object detection
|
||||
x.detector_fps_object = '2'
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
|
||||
if(e.details.detector_scale_x_object&&e.details.detector_scale_x_object!==''&&e.details.detector_scale_y_object&&e.details.detector_scale_y_object!==''){x.dobjratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object}else{x.dobjratio=x.dratio}
|
||||
x.pipe += ' -r ' + x.detector_fps + x.dobjratio + x.cust_detect
|
||||
if(e.details.detector_fps_object){x.detector_fps_object = e.details.detector_fps_object}
|
||||
x.pipe += ' -r ' + x.detector_fps_object + x.dobjratio + x.cust_detect
|
||||
if(e.details.detector_h264 === '1'){
|
||||
x.pipe += h264Output
|
||||
}else{
|
||||
|
@ -884,6 +907,46 @@ module.exports = function(s,config,onFinish){
|
|||
x.pipe += ' -q:v 1 -an -c:v copy -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'coProcessor.m3u8"'
|
||||
}
|
||||
}
|
||||
ffmpeg.buildTimelapseOutput = function(e,x){
|
||||
if(e.details.record_timelapse === '1'){
|
||||
x.record_timelapse_video_filters = []
|
||||
if(e.details.input_map_choices&&e.details.input_map_choices.record_timelapse){
|
||||
//add input feed map
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.record_timelapse)
|
||||
}
|
||||
var flags = []
|
||||
if(e.details.record_timelapse_fps && e.details.record_timelapse_fps !== ''){
|
||||
flags.push('-r 1/' + e.details.record_timelapse_fps)
|
||||
}else{
|
||||
flags.push('-r 1/900') // 15 minutes
|
||||
}
|
||||
if(e.details.record_timelapse_vf && e.details.record_timelapse_vf !== '')flags.push('-vf ' + e.details.record_timelapse_vf)
|
||||
if(e.details.record_timelapse_scale_x && e.details.record_timelapse_scale_x !== '' && e.details.record_timelapse_scale_y && e.details.record_timelapse_scale_y !== '')flags.push(`-s ${e.details.record_timelapse_scale_x}x${e.details.record_timelapse_scale_y}`)
|
||||
//record - watermark for -vf
|
||||
if(e.details.record_timelapse_watermark&&e.details.record_timelapse_watermark=="1"&&e.details.record_timelapse_watermark_location&&e.details.record_timelapse_watermark_location!==''){
|
||||
switch(e.details.record_timelapse_watermark_position){
|
||||
case'tl'://top left
|
||||
x.record_timelapse_watermark_position='10:10'
|
||||
break;
|
||||
case'tr'://top right
|
||||
x.record_timelapse_watermark_position='main_w-overlay_w-10:10'
|
||||
break;
|
||||
case'bl'://bottom left
|
||||
x.record_timelapse_watermark_position='10:main_h-overlay_h-10'
|
||||
break;
|
||||
default://bottom right
|
||||
x.record_timelapse_watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2'
|
||||
break;
|
||||
}
|
||||
x.record_timelapse_video_filters.push('movie='+e.details.record_timelapse_watermark_location+'[watermark],[in][watermark]overlay='+x.record_timelapse_watermark_position+'[out]');
|
||||
}
|
||||
if(x.record_timelapse_video_filters.length > 0){
|
||||
var videoFilter = `-vf "${x.record_timelapse_video_filters.join(',').trim()}"`
|
||||
flags.push(videoFilter)
|
||||
}
|
||||
x.pipe += ` -f singlejpeg ${flags.join(' ')} -an -q:v 1 pipe:7`
|
||||
}
|
||||
}
|
||||
ffmpeg.assembleMainPieces = function(e,x){
|
||||
//create executeable FFMPEG command
|
||||
x.ffmpegCommandString = x.loglevel+x.input_fps;
|
||||
|
@ -900,6 +963,9 @@ module.exports = function(s,config,onFinish){
|
|||
case'mjpeg':
|
||||
x.ffmpegCommandString += ' -reconnect 1 -f mjpeg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"';
|
||||
break;
|
||||
case'mxpeg':
|
||||
x.ffmpegCommandString += ' -reconnect 1 -f mxg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"';
|
||||
break;
|
||||
case'rtmp':
|
||||
if(!e.details.rtmp_key)e.details.rtmp_key = ''
|
||||
x.ffmpegCommandString += x.cust_input+x.hwaccel+` -i "rtmp://127.0.0.1:1935/${e.ke + '_' + e.mid + '_' + e.details.rtmp_key}"`;
|
||||
|
@ -941,13 +1007,14 @@ module.exports = function(s,config,onFinish){
|
|||
ffmpeg.buildAudioDetector(e,x)
|
||||
ffmpeg.buildMainDetector(e,x)
|
||||
ffmpeg.buildCoProcessorFeed(e,x)
|
||||
ffmpeg.buildTimelapseOutput(e,x)
|
||||
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
|
||||
extender(e,x)
|
||||
})
|
||||
ffmpeg.assembleMainPieces(e,x)
|
||||
ffmpeg.createPipeArray(e,x)
|
||||
//hold ffmpeg command for log stream
|
||||
s.group[e.ke].mon[e.mid].ffmpeg = x.ffmpegCommandString
|
||||
s.group[e.ke].activeMonitors[e.mid].ffmpeg = x.ffmpegCommandString
|
||||
//clean the string of spatial impurities and split for spawn()
|
||||
x.ffmpegCommandString = s.splitForFFPMEG(x.ffmpegCommandString)
|
||||
//launch that bad boy
|
||||
|
|
|
@ -140,27 +140,27 @@ module.exports = function(s,config,lang,ffmpeg){
|
|||
if(commandString === x.input){
|
||||
return false
|
||||
}
|
||||
s.group[e.ke].mon[e.mid].coProcessorCmd = commandString
|
||||
s.group[e.ke].activeMonitors[e.mid].coProcessorCmd = commandString
|
||||
return spawn(config.ffmpegDir,s.splitForFFPMEG((commandString).replace(/\s+/g,' ').trim()),{detached: true,stdio:x.stdioPipes})
|
||||
}
|
||||
s.coSpawnLauncher = function(e){
|
||||
if(s.group[e.ke].mon[e.id].isStarted === true && e.coProcessor === true){
|
||||
if(s.group[e.ke].activeMonitors[e.id].isStarted === true && e.coProcessor === true){
|
||||
s.coSpawnClose(e)
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e)
|
||||
if(s.group[e.ke].mon[e.id].coSpawnProcessor === false){
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e)
|
||||
if(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor === false){
|
||||
return
|
||||
}
|
||||
s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}});
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessorExit = function(){
|
||||
s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}});
|
||||
s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].activeMonitors[e.id].coProcessorCmd}});
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit = function(){
|
||||
s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].activeMonitors[e.id].coProcessorCmd}});
|
||||
setTimeout(function(){
|
||||
s.coSpawnLauncher(e)
|
||||
},2000)
|
||||
}
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.on('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit)
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.on('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit)
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.on('end',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit)
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.on('exit',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit)
|
||||
var checkLog = function(d,x){return d.indexOf(x)>-1;}
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stderr.on('data',function(d){
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stderr.on('data',function(d){
|
||||
d=d.toString();
|
||||
switch(true){
|
||||
case checkLog(d,'deprecated pixel format used'):
|
||||
|
@ -176,34 +176,34 @@ module.exports = function(s,config,lang,ffmpeg){
|
|||
s.userLog(e,{type:lang.coProcessor,msg:d});
|
||||
})
|
||||
if(e.frame_to_stream){
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream)
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream)
|
||||
}
|
||||
if(e.details.detector === '1'){
|
||||
s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke})
|
||||
//frames from motion detect
|
||||
if(e.details.detector_pam === '1'){
|
||||
s.createPamDiffEngine(e)
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].mon[e.id].p2p).pipe(s.group[e.ke].mon[e.id].pamDiff)
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff)
|
||||
if(e.details.detector_use_detect_object === '1'){
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[4].on('data',function(d){
|
||||
s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[4].on('data',function(d){
|
||||
s.group[e.ke].activeMonitors[e.id].lastJpegDetectorFrame = d
|
||||
})
|
||||
}
|
||||
}else if(s.isAtleatOneDetectorPluginConnected){
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].on('data',function(d){
|
||||
s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d});
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[3].on('data',function(d){
|
||||
s.ocvTx({f:'frame',mon:s.group[e.ke].rawMonitorConfigurations[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d});
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.coSpawnClose = function(e){
|
||||
if(s.group[e.ke].mon[e.id].coSpawnProcessor){
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit);
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit);
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.stdin.pause()
|
||||
s.group[e.ke].mon[e.id].coSpawnProcessor.kill()
|
||||
delete(s.group[e.ke].mon[e.id].coSpawnProcessor)
|
||||
if(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor){
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit);
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit);
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdin.pause()
|
||||
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.kill()
|
||||
delete(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor)
|
||||
s.userLog(e,{type:lang['coProcessor Stopped'],msg:{msg:lang.coProcessorTextStopped+' : '+e.id}});
|
||||
}
|
||||
}
|
||||
|
|
13
libs/fileBin.js
Normal file
13
libs/fileBin.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
var fs = require('fs')
|
||||
var moment = require('moment')
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
s.getFileBinDirectory = function(e){
|
||||
if(e.mid&&!e.id){e.id=e.mid}
|
||||
s.checkDetails(e)
|
||||
if(e.details&&e.details.dir&&e.details.dir!==''){
|
||||
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'/'
|
||||
}else{
|
||||
return s.dir.fileBin+e.ke+'/'+e.id+'/';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
var fs = require('fs');
|
||||
module.exports = function(s,config){
|
||||
module.exports = function(s,config,lang){
|
||||
//directories
|
||||
s.group = {}
|
||||
if(!config.windowsTempDir&&s.isWin===true){config.windowsTempDir='C:/Windows/Temp'}
|
||||
|
@ -40,10 +40,44 @@ module.exports = function(s,config){
|
|||
fs.mkdirSync(s.dir.fileBin);
|
||||
}
|
||||
//additional storage areas
|
||||
s.listOfStorage = [{
|
||||
name: lang['Default'],
|
||||
value: ""
|
||||
}]
|
||||
s.dir.addStorage.forEach(function(v,n){
|
||||
v.path = s.checkCorrectPathEnding(v.path)
|
||||
if(!fs.existsSync(v.path)){
|
||||
fs.mkdirSync(v.path);
|
||||
}
|
||||
s.listOfStorage.push({
|
||||
name: v.name,
|
||||
value: v.path
|
||||
})
|
||||
})
|
||||
//get audio files list
|
||||
s.listOfAudioFiles = [
|
||||
{
|
||||
name:lang['No Sound'],
|
||||
value:""
|
||||
}
|
||||
]
|
||||
fs.readdirSync(s.mainDirectory + '/web/libs/audio').forEach(function(file){
|
||||
s.listOfAudioFiles.push({
|
||||
name: file,
|
||||
value: file
|
||||
})
|
||||
})
|
||||
//get themes list
|
||||
s.listOfThemes = [
|
||||
{
|
||||
name:lang['Default'],
|
||||
value:""
|
||||
}
|
||||
]
|
||||
fs.readdirSync(s.mainDirectory + '/web/libs/themes').forEach(function(folder){
|
||||
s.listOfThemes.push({
|
||||
name: folder,
|
||||
value: folder
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
var exec = require('child_process').exec;
|
||||
var spawn = require('child_process').spawn;
|
||||
module.exports = function(s,config,lang,io){
|
||||
s.sendDiskUsedAmountToClients = function(e){
|
||||
//send the amount used disk space to connected users
|
||||
if(s.group[e.ke]&&s.group[e.ke].init){
|
||||
s.tx({f:'diskUsed',size:s.group[e.ke].usedSpace,limit:s.group[e.ke].sizeLimit},'GRP_'+e.ke);
|
||||
}
|
||||
}
|
||||
s.heartBeat = function(){
|
||||
setTimeout(s.heartBeat, 8000);
|
||||
io.sockets.emit('ping',{beat:1});
|
||||
|
@ -34,14 +28,20 @@ module.exports = function(s,config,lang,io){
|
|||
d = d.replace(/(\r\n|\n|\r)/gm, "").replace(/%/g, "")
|
||||
}
|
||||
callback(d)
|
||||
});
|
||||
s.onGetCpuUsageExtensions.forEach(function(extender){
|
||||
extender(d)
|
||||
})
|
||||
})
|
||||
} else if(k.cmd){
|
||||
exec(k.cmd,{encoding:'utf8',detached: true},function(err,d){
|
||||
if(s.isWin===true){
|
||||
d=d.replace(/(\r\n|\n|\r)/gm,"").replace(/%/g,"")
|
||||
}
|
||||
callback(d)
|
||||
});
|
||||
s.onGetCpuUsageExtensions.forEach(function(extender){
|
||||
extender(d)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
callback(0)
|
||||
}
|
||||
|
@ -68,7 +68,10 @@ module.exports = function(s,config,lang,io){
|
|||
d=(parseInt(d.split('=')[1])/(s.totalmem/1000))*100
|
||||
}
|
||||
callback(d)
|
||||
});
|
||||
s.onGetRamUsageExtensions.forEach(function(extender){
|
||||
extender(d)
|
||||
})
|
||||
})
|
||||
}else{
|
||||
callback(0)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
var fs = require('fs')
|
||||
module.exports = function(s,config){
|
||||
if(!config.language){
|
||||
config.language='en_CA'
|
||||
|
@ -9,19 +10,19 @@ module.exports = function(s,config){
|
|||
console.log('There was an error loading your language file.')
|
||||
var lang = require(s.location.languages+'/en_CA.json');
|
||||
}
|
||||
s.location.definitions = s.mainDirectory+'/definitions'
|
||||
try{
|
||||
var definitions = require(s.location.definitions+'/'+config.language+'.json');
|
||||
}catch(er){
|
||||
console.error(er)
|
||||
console.log('There was an error loading your language file.')
|
||||
var definitions = require(s.location.definitions+'/en_CA.json');
|
||||
}
|
||||
//load languages dynamically
|
||||
s.copySystemDefaultLanguage = function(){
|
||||
//en_CA
|
||||
return Object.assign(lang,{})
|
||||
}
|
||||
s.listOfPossibleLanguages = []
|
||||
fs.readdirSync(s.mainDirectory + '/languages').forEach(function(filename){
|
||||
var name = filename.replace('.json','')
|
||||
s.listOfPossibleLanguages.push({
|
||||
"name": name,
|
||||
"value": name,
|
||||
})
|
||||
})
|
||||
s.loadedLanguages={}
|
||||
s.loadedLanguages[config.language] = s.copySystemDefaultLanguage()
|
||||
s.getLanguageFile = function(rule){
|
||||
|
@ -41,29 +42,5 @@ module.exports = function(s,config){
|
|||
}
|
||||
return file
|
||||
}
|
||||
//load defintions dynamically
|
||||
s.copySystemDefaultDefinitions = function(){
|
||||
//en_CA
|
||||
return Object.assign(definitions,{})
|
||||
}
|
||||
s.loadedDefinitons={}
|
||||
s.loadedDefinitons[config.language] = s.copySystemDefaultDefinitions()
|
||||
s.getDefinitonFile = function(rule){
|
||||
if(rule && rule !== ''){
|
||||
var file = s.loadedDefinitons[file]
|
||||
if(!file){
|
||||
try{
|
||||
s.loadedDefinitons[rule] = require(s.location.definitions+'/'+rule+'.json')
|
||||
s.loadedDefinitons[rule] = Object.assign(s.copySystemDefaultDefinitions(),s.loadedDefinitons[rule])
|
||||
file = s.loadedDefinitons[rule]
|
||||
}catch(err){
|
||||
file = s.copySystemDefaultDefinitions()
|
||||
}
|
||||
}
|
||||
}else{
|
||||
file = s.copySystemDefaultDefinitions()
|
||||
}
|
||||
return file
|
||||
}
|
||||
return lang
|
||||
}
|
||||
|
|
1195
libs/monitor.js
1195
libs/monitor.js
File diff suppressed because it is too large
Load diff
|
@ -50,7 +50,7 @@ module.exports = function(s,config,lang){
|
|||
var onEventTriggerForDiscord = function(d,filter){
|
||||
// d = event object
|
||||
//discord bot
|
||||
if(filter.discord && d.mon.details.detector_discordbot === '1' && !s.group[d.ke].mon[d.id].detector_discordbot){
|
||||
if(filter.discord && s.group[d.ke].discordBot && d.mon.details.detector_discordbot === '1' && !s.group[d.ke].activeMonitors[d.id].detector_discordbot){
|
||||
var detector_discordbot_timeout
|
||||
if(!d.mon.details.detector_discordbot_timeout||d.mon.details.detector_discordbot_timeout===''){
|
||||
detector_discordbot_timeout = 1000*60*10;
|
||||
|
@ -58,16 +58,16 @@ module.exports = function(s,config,lang){
|
|||
detector_discordbot_timeout = parseFloat(d.mon.details.detector_discordbot_timeout)*1000*60;
|
||||
}
|
||||
//lock mailer so you don't get emailed on EVERY trigger event.
|
||||
s.group[d.ke].mon[d.id].detector_discordbot=setTimeout(function(){
|
||||
s.group[d.ke].activeMonitors[d.id].detector_discordbot = setTimeout(function(){
|
||||
//unlock so you can mail again.
|
||||
clearTimeout(s.group[d.ke].mon[d.id].detector_discordbot);
|
||||
delete(s.group[d.ke].mon[d.id].detector_discordbot);
|
||||
},detector_discordbot_timeout);
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_discordbot);
|
||||
delete(s.group[d.ke].activeMonitors[d.id].detector_discordbot);
|
||||
},detector_discordbot_timeout)
|
||||
var files = []
|
||||
var sendAlert = function(){
|
||||
s.discordMsg({
|
||||
author: {
|
||||
name: s.group[d.ke].mon_conf[d.id].name,
|
||||
name: s.group[d.ke].rawMonitorConfigurations[d.id].name,
|
||||
icon_url: config.iconURL
|
||||
},
|
||||
title: lang.Event+' - '+d.screenshotName,
|
||||
|
@ -84,7 +84,7 @@ module.exports = function(s,config,lang){
|
|||
s.mergeDetectorBufferChunks(d,function(mergedFilepath,filename){
|
||||
s.discordMsg({
|
||||
author: {
|
||||
name: s.group[d.ke].mon_conf[d.id].name,
|
||||
name: s.group[d.ke].rawMonitorConfigurations[d.id].name,
|
||||
icon_url: config.iconURL
|
||||
},
|
||||
title: filename,
|
||||
|
@ -102,8 +102,10 @@ module.exports = function(s,config,lang){
|
|||
],d.ke)
|
||||
})
|
||||
}
|
||||
s.getRawSnapshotFromMonitor(d.mon,function(data){
|
||||
if((data[data.length-2] === 0xFF && data[data.length-1] === 0xD9)){
|
||||
s.getRawSnapshotFromMonitor(d.mon,{
|
||||
secondsInward: d.mon.details.snap_seconds_inward
|
||||
},function(data){
|
||||
if(data[data.length - 2] === 0xFF && data[data.length - 1] === 0xD9){
|
||||
d.screenshotBuffer = data
|
||||
files.push({
|
||||
attachment: d.screenshotBuffer,
|
||||
|
@ -251,7 +253,7 @@ module.exports = function(s,config,lang){
|
|||
filter.mail = true
|
||||
}
|
||||
var onEventTriggerForEmail = function(d,filter){
|
||||
if(filter.mail && config.mail && !s.group[d.ke].mon[d.id].detector_mail && d.mon.details.detector_mail === '1'){
|
||||
if(filter.mail && config.mail && !s.group[d.ke].activeMonitors[d.id].detector_mail && d.mon.details.detector_mail === '1'){
|
||||
s.sqlQuery('SELECT mail FROM Users WHERE ke=? AND details NOT LIKE ?',[d.ke,'%"sub"%'],function(err,r){
|
||||
r=r[0];
|
||||
var detector_mail_timeout
|
||||
|
@ -261,10 +263,10 @@ module.exports = function(s,config,lang){
|
|||
detector_mail_timeout = parseFloat(d.mon.details.detector_mail_timeout)*1000*60;
|
||||
}
|
||||
//lock mailer so you don't get emailed on EVERY trigger event.
|
||||
s.group[d.ke].mon[d.id].detector_mail=setTimeout(function(){
|
||||
s.group[d.ke].activeMonitors[d.id].detector_mail=setTimeout(function(){
|
||||
//unlock so you can mail again.
|
||||
clearTimeout(s.group[d.ke].mon[d.id].detector_mail);
|
||||
delete(s.group[d.ke].mon[d.id].detector_mail);
|
||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_mail);
|
||||
delete(s.group[d.ke].activeMonitors[d.id].detector_mail);
|
||||
},detector_mail_timeout);
|
||||
var files = []
|
||||
var mailOptions = {
|
||||
|
@ -287,36 +289,42 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
if(d.mon.details.detector_mail_send_video === '1'){
|
||||
s.mergeDetectorBufferChunks(d,function(mergedFilepath,filename){
|
||||
s.nodemailer.sendMail({
|
||||
from: config.mail.from,
|
||||
to: r.mail,
|
||||
subject: filename,
|
||||
html: '',
|
||||
attachments: [
|
||||
{
|
||||
filename: filename,
|
||||
content: fs.readFileSync(mergedFilepath)
|
||||
}
|
||||
]
|
||||
}, (error, info) => {
|
||||
if (error) {
|
||||
s.systemLog(lang.MailError,error)
|
||||
return false;
|
||||
fs.readFile(mergedFilepath,function(err,buffer){
|
||||
if(buffer){
|
||||
s.nodemailer.sendMail({
|
||||
from: config.mail.from,
|
||||
to: r.mail,
|
||||
subject: filename,
|
||||
html: '',
|
||||
attachments: [
|
||||
{
|
||||
filename: filename,
|
||||
content: buffer
|
||||
}
|
||||
]
|
||||
}, (error, info) => {
|
||||
if (error) {
|
||||
s.systemLog(lang.MailError,error)
|
||||
return false;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if(d.screenshotBuffer){
|
||||
files.push({
|
||||
filename: d.screenshotName+'.jpg',
|
||||
filename: d.screenshotName + '.jpg',
|
||||
content: d.screenshotBuffer
|
||||
})
|
||||
sendMail()
|
||||
}else{
|
||||
s.getRawSnapshotFromMonitor(d.mon,function(data){
|
||||
s.getRawSnapshotFromMonitor(d.mon,{
|
||||
secondsInward: d.mon.details.snap_seconds_inward
|
||||
},function(data){
|
||||
d.screenshotBuffer = data
|
||||
files.push({
|
||||
filename: d.screenshotName+'.jpg',
|
||||
filename: d.screenshotName + '.jpg',
|
||||
content: data
|
||||
})
|
||||
sendMail()
|
||||
|
|
107
libs/plugins.js
107
libs/plugins.js
|
@ -1,7 +1,12 @@
|
|||
var socketIOclient = require('socket.io-client');
|
||||
module.exports = function(s,config,lang){
|
||||
module.exports = function(s,config,lang,io){
|
||||
//send data to detector plugin
|
||||
s.ocvTx = function(data){
|
||||
// chaining coming in future update
|
||||
s.sendToAllDetectors(data)
|
||||
}
|
||||
//function for receiving detector data
|
||||
s.pluginEventController=function(d){
|
||||
s.pluginEventController = function(d){
|
||||
switch(d.f){
|
||||
case'trigger':
|
||||
s.triggerEvent(d)
|
||||
|
@ -22,16 +27,32 @@ module.exports = function(s,config,lang){
|
|||
s.detectorPluginArray = []
|
||||
s.isAtleatOneDetectorPluginConnected = false
|
||||
s.addDetectorPlugin = function(name,d){
|
||||
if(config.useOldPluginConnectionMethod === true){
|
||||
s.ocv = {
|
||||
started: s.timeObject(),
|
||||
id: d.id,
|
||||
plug: d.plug,
|
||||
notice: d.notice,
|
||||
isClientPlugin: d.isClientPlugin,
|
||||
isHostPlugin: d.isHostPlugin,
|
||||
connectionType: d.connectionType
|
||||
}
|
||||
}
|
||||
s.connectedDetectorPlugins[d.plug] = {
|
||||
started: s.timeObject(),
|
||||
id: d.id,
|
||||
plug: d.plug,
|
||||
notice: d.notice,
|
||||
isClientPlugin: d.isClientPlugin,
|
||||
isHostPlugin: d.isHostPlugin,
|
||||
connectionType: d.connectionType
|
||||
}
|
||||
s.resetDetectorPluginArray()
|
||||
}
|
||||
s.removeDetectorPlugin = function(name){
|
||||
if(config.oldPluginConnectionMethod === true && s.ocv && s.ocv.plug === name){
|
||||
delete(s.ocv)
|
||||
}
|
||||
delete(s.connectedDetectorPlugins[name])
|
||||
s.resetDetectorPluginArray(name)
|
||||
}
|
||||
|
@ -48,6 +69,7 @@ module.exports = function(s,config,lang){
|
|||
}else{
|
||||
s.isAtleatOneDetectorPluginConnected = false
|
||||
}
|
||||
s.debugLog(`resetDetectorPluginArray : ${JSON.stringify(pluginArray)}`)
|
||||
s.detectorPluginArray = pluginArray
|
||||
}
|
||||
s.sendToAllDetectors = function(data){
|
||||
|
@ -67,7 +89,7 @@ module.exports = function(s,config,lang){
|
|||
// s.sendToDetectorsInChain = function(){
|
||||
//
|
||||
// }
|
||||
s.pluginInitiatorSuccess=function(mode,d,cn){
|
||||
s.pluginInitiatorSuccess = function(mode,d,cn){
|
||||
s.systemLog('pluginInitiatorSuccess',d)
|
||||
if(!s.connectedPlugins[d.plug]){
|
||||
s.connectedPlugins[d.plug]={
|
||||
|
@ -83,6 +105,7 @@ module.exports = function(s,config,lang){
|
|||
s.systemLog('Connected to plugin : Detector - '+d.plug+' - '+d.type)
|
||||
switch(d.type){
|
||||
default:case'detector':
|
||||
if(config.oldPluginConnectionMethod)cn.ocv = 1
|
||||
cn.detectorPlugin = d.plug
|
||||
s.addDetectorPlugin(d.plug,{
|
||||
id: cn.id,
|
||||
|
@ -99,10 +122,10 @@ module.exports = function(s,config,lang){
|
|||
switch(d.type){
|
||||
default:case'detector':
|
||||
s.addDetectorPlugin(d.plug,{
|
||||
id:"host",
|
||||
plug:d.plug,
|
||||
notice:d.notice,
|
||||
isHostPlugin:true,
|
||||
id: "host",
|
||||
plug: d.plug,
|
||||
notice: d.notice,
|
||||
isHostPlugin: true,
|
||||
connectionType: d.connectionType
|
||||
})
|
||||
s.tx({f:'detector_plugged',plug:d.plug,notice:d.notice},'CPU')
|
||||
|
@ -145,12 +168,12 @@ module.exports = function(s,config,lang){
|
|||
});
|
||||
socket.on('init',function(d){
|
||||
s.systemLog('Initialize Plugin : Host',d)
|
||||
if(d.ok===true){
|
||||
if(d.ok === true){
|
||||
s.pluginInitiatorSuccess("host",d)
|
||||
}else{
|
||||
s.pluginInitiatorFail("host",d)
|
||||
}
|
||||
});
|
||||
})
|
||||
socket.on('ocv',s.pluginEventController);
|
||||
socket.on('disconnect', function(){
|
||||
s.connectedPlugins[v.id].plugged=false
|
||||
|
@ -174,4 +197,70 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
})
|
||||
}
|
||||
var onWebSocketDisconnection = function(cn){
|
||||
if(cn.pluginEngine){
|
||||
s.connectedPlugins[cn.pluginEngine].plugged = false
|
||||
s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU')
|
||||
}
|
||||
if(cn.detectorPlugin){
|
||||
s.tx({f:'detector_unplugged',plug:cn.detectorPlugin},'CPU')
|
||||
s.removeDetectorPlugin(cn.detectorPlugin)
|
||||
s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){
|
||||
s.tx(data,'CPU')
|
||||
})
|
||||
}
|
||||
if(cn.ocv && s.ocv){
|
||||
s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU')
|
||||
delete(s.ocv);
|
||||
}
|
||||
}
|
||||
var onSocketAuthentication = function(r,cn,d,tx){
|
||||
if(s.isAtleatOneDetectorPluginConnected){
|
||||
s.sendDetectorInfoToClient({f:'detector_plugged'},tx)
|
||||
s.ocvTx({f:'readPlugins',ke:d.ke})
|
||||
}
|
||||
if(config.oldPluginConnectionMethod && s.ocv){
|
||||
tx({f:'detector_plugged',plug:s.ocv.plug,notice:s.ocv.notice})
|
||||
}
|
||||
}
|
||||
var onWebSocketConnection = function(cn){
|
||||
cn.on('ocv',function(d){
|
||||
if(!cn.pluginEngine && d.f === 'init'){
|
||||
if(config.pluginKeys[d.plug] === d.pluginKey){
|
||||
s.pluginInitiatorSuccess("client",d,cn)
|
||||
}else{
|
||||
s.pluginInitiatorFail("client",d,cn)
|
||||
}
|
||||
}else{
|
||||
if(config.pluginKeys[d.plug] === d.pluginKey){
|
||||
s.pluginEventController(d)
|
||||
}else{
|
||||
cn.disconnect()
|
||||
}
|
||||
}
|
||||
})
|
||||
cn.on('f',function(d){
|
||||
if((d.id || d.uid || d.mid) && cn.ke){
|
||||
switch(d.f){
|
||||
case'ocv_in':
|
||||
s.ocvTx(d.data)
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if(config.oldPluginConnectionMethod === undefined)config.oldPluginConnectionMethod = false
|
||||
if(config.oldPluginConnectionMethod === true){
|
||||
s.ocvTx = function(data){
|
||||
if(!s.ocv){return}
|
||||
if(s.ocv.isClientPlugin === true){
|
||||
s.tx(data,s.ocv.id)
|
||||
}else{
|
||||
s.connectedPlugins[s.ocv.plug].tx(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.onSocketAuthentication(onSocketAuthentication)
|
||||
s.onWebSocketDisconnection(onWebSocketDisconnection)
|
||||
s.onWebSocketConnection(onWebSocketConnection)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var os = require('os')
|
||||
module.exports = function(process,__dirname){
|
||||
var packageJson = require('../package.json')
|
||||
process.send = process.send || function () {};
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.error('Uncaught Exception occured!');
|
||||
|
@ -32,5 +33,9 @@ module.exports = function(process,__dirname){
|
|||
//directory path for this file
|
||||
mainDirectory : __dirname
|
||||
}
|
||||
s.packageJson = packageJson
|
||||
if(packageJson.mainDirectory){
|
||||
s.mainDirectory = require('path').resolve('.')
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const chalk = require('chalk');
|
||||
|
||||
LOG_TYPES = {
|
||||
NONE: 0,
|
||||
ERROR: 1,
|
||||
|
@ -24,25 +22,25 @@ const logTime = () => {
|
|||
const log = (...args) => {
|
||||
if (logType < LOG_TYPES.NORMAL) return;
|
||||
|
||||
console.log(logTime(), process.pid, chalk.bold.green('[INFO]'), ...args);
|
||||
console.log(logTime(), ...args);
|
||||
};
|
||||
|
||||
const error = (...args) => {
|
||||
if (logType < LOG_TYPES.ERROR) return;
|
||||
|
||||
console.log(logTime(), process.pid, chalk.bold.red('[ERROR]'), ...args);
|
||||
console.error(logTime(), ...args);
|
||||
};
|
||||
|
||||
const debug = (...args) => {
|
||||
if (logType < LOG_TYPES.DEBUG) return;
|
||||
|
||||
console.log(logTime(), process.pid, chalk.bold.blue('[DEBUG]'), ...args);
|
||||
console.log(logTime(), ...args);
|
||||
};
|
||||
|
||||
const ffdebug = (...args) => {
|
||||
if (logType < LOG_TYPES.FFDEBUG) return;
|
||||
|
||||
console.log(logTime(), process.pid, chalk.bold.blue('[FFDEBUG]'), ...args);
|
||||
console.log(logTime(), ...args);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
@ -50,4 +48,4 @@ module.exports = {
|
|||
setLogType,
|
||||
|
||||
log, error, debug, ffdebug
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class NodeRtmpServer {
|
|||
session.socket.destroy();
|
||||
context.sessions.delete(id);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
var schedule = Object.assign(row,{})
|
||||
if(!s.schedules[schedule.ke])s.schedules[schedule.ke] = {}
|
||||
s.checkDetails(schedule)
|
||||
schedule.timezoneOffset = parseInt(schedule.details.timezone) || 0
|
||||
schedule.details.days.forEach(function(dayNumber,key){
|
||||
schedule.details.days[key] = parseInt(dayNumber)
|
||||
})
|
||||
if(!s.schedules[schedule.ke][schedule.name]){
|
||||
s.schedules[schedule.ke][schedule.name] = schedule
|
||||
}else{
|
||||
|
@ -22,7 +26,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
}
|
||||
//check time in schedule
|
||||
s.checkTimeAgainstSchedule = function(start,end,callback){
|
||||
var checkTimeAgainstSchedule = function(schedule,callback){
|
||||
var start = schedule.start
|
||||
var end = schedule.end
|
||||
if(!callback)callback = function(){}
|
||||
try{
|
||||
if(
|
||||
start
|
||||
|
@ -32,16 +39,17 @@ module.exports = function(s,config,lang,app,io){
|
|||
var startHour = parseInt(startSplit[0])
|
||||
var startMin = parseInt(startSplit[1])
|
||||
checkStartTime.setHours(startHour)
|
||||
checkStartTime.setMinutes(startMin)
|
||||
checkStartTime.setMinutes(startMin - schedule.timezoneOffset)
|
||||
if(end){
|
||||
var checkEndTime = new Date()
|
||||
var endSplit = end.split(':')
|
||||
var endHour = parseInt(endSplit[0])
|
||||
var endMin = parseInt(endSplit[1])
|
||||
checkEndTime.setHours(endHour)
|
||||
checkEndTime.setMinutes(endMin)
|
||||
checkEndTime.setMinutes(endMin - schedule.timezoneOffset)
|
||||
}
|
||||
var currentDate = new Date()
|
||||
currentDate.setMinutes(currentDate.getMinutes() - schedule.timezoneOffset)
|
||||
if(
|
||||
(
|
||||
currentDate >= checkStartTime &&
|
||||
|
@ -50,6 +58,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
currentDate >= checkStartTime && !end
|
||||
){
|
||||
callback()
|
||||
return true
|
||||
}else{
|
||||
callback({
|
||||
currentDate : currentDate,
|
||||
|
@ -58,12 +67,45 @@ module.exports = function(s,config,lang,app,io){
|
|||
})
|
||||
}
|
||||
}else{
|
||||
callback()
|
||||
callback({}) //no start time selected, failed
|
||||
}
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
callback()
|
||||
callback(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
//check days in schedule
|
||||
var checkDaysAgainstSchedule = function(schedule,callback){
|
||||
var days = schedule.details.days
|
||||
if(!callback)callback = function(){}
|
||||
try{
|
||||
if(
|
||||
days
|
||||
){
|
||||
var currentDate = new Date()
|
||||
currentDate.setMinutes(currentDate.getMinutes() - schedule.timezoneOffset)
|
||||
var currentDay = currentDate.getDay()
|
||||
if(
|
||||
days.indexOf(currentDay) > -1 // if currentDay of week is found in schedule selection
|
||||
){
|
||||
callback()
|
||||
return true
|
||||
}else{
|
||||
callback({
|
||||
currentDate: currentDate,
|
||||
currentDay: currentDay
|
||||
})
|
||||
}
|
||||
}else{
|
||||
callback() //no days selected, succeed
|
||||
return true
|
||||
}
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
callback(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
//check all Schedules
|
||||
s.checkSchedules = function(v,callback){
|
||||
|
@ -73,25 +115,25 @@ module.exports = function(s,config,lang,app,io){
|
|||
scheduleNames.forEach(function(name){
|
||||
var schedule = s.schedules[key][name]
|
||||
if(!schedule.active && schedule.enabled === 1 && schedule.start && schedule.details.monitorStates){
|
||||
s.checkTimeAgainstSchedule(schedule.start,schedule.end,function(err){
|
||||
if(!err){
|
||||
schedule.active = true
|
||||
var monitorStates = schedule.details.monitorStates
|
||||
monitorStates.forEach(function(stateName){
|
||||
s.activateMonitorStates(key,stateName,{
|
||||
ke: key,
|
||||
uid: 'System',
|
||||
details: {},
|
||||
permissions: {},
|
||||
lang: lang
|
||||
},function(endData){
|
||||
// console.log(endData)
|
||||
})
|
||||
var timePasses = checkTimeAgainstSchedule(schedule)
|
||||
var daysPasses = checkDaysAgainstSchedule(schedule)
|
||||
if(timePasses && daysPasses){
|
||||
schedule.active = true
|
||||
var monitorStates = schedule.details.monitorStates
|
||||
monitorStates.forEach(function(stateName){
|
||||
s.activateMonitorStates(key,stateName,{
|
||||
ke: key,
|
||||
uid: 'System',
|
||||
details: {},
|
||||
permissions: {},
|
||||
lang: lang
|
||||
},function(endData){
|
||||
// console.log(endData)
|
||||
})
|
||||
}else{
|
||||
schedule.active = false
|
||||
}
|
||||
})
|
||||
})
|
||||
}else{
|
||||
schedule.active = false
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
113
libs/socketio.js
113
libs/socketio.js
|
@ -7,11 +7,6 @@ var jsonfile = require("jsonfile");
|
|||
var onvif = require("node-onvif");
|
||||
module.exports = function(s,config,lang,io){
|
||||
s.clientSocketConnection = {}
|
||||
//send data to detector plugin
|
||||
s.ocvTx=function(data){
|
||||
// chaining coming in future update
|
||||
s.sendToAllDetectors(data)
|
||||
}
|
||||
//send data to socket client function
|
||||
s.tx = function(z,y,x){if(x){return x.broadcast.to(y).emit('f',z)};io.to(y).emit('f',z);}
|
||||
s.txToDashcamUsers = function(data,groupKey){
|
||||
|
@ -53,25 +48,9 @@ module.exports = function(s,config,lang,io){
|
|||
////socket controller
|
||||
io.on('connection', function (cn) {
|
||||
var tx;
|
||||
//set "client" detector plugin event function
|
||||
cn.on('ocv',function(d){
|
||||
if(!cn.pluginEngine && d.f === 'init'){
|
||||
if(config.pluginKeys[d.plug] === d.pluginKey){
|
||||
s.pluginInitiatorSuccess("client",d,cn)
|
||||
}else{
|
||||
s.pluginInitiatorFail("client",d,cn)
|
||||
}
|
||||
}else{
|
||||
if(config.pluginKeys[d.plug] === d.pluginKey){
|
||||
s.pluginEventController(d)
|
||||
}else{
|
||||
cn.disconnect()
|
||||
}
|
||||
}
|
||||
})
|
||||
//unique h265 socket stream
|
||||
cn.on('h265',function(d){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
|
||||
cn.disconnect();return;
|
||||
}
|
||||
cn.ip=cn.request.connection.remoteAddress;
|
||||
|
@ -87,10 +66,10 @@ module.exports = function(s,config,lang,io){
|
|||
r=r[0];
|
||||
var Emitter,chunkChannel
|
||||
if(!d.channel){
|
||||
Emitter = s.group[d.ke].mon[d.id].emitter
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(d.channel)+config.pipeAddition
|
||||
}
|
||||
if(!Emitter){
|
||||
|
@ -144,7 +123,7 @@ module.exports = function(s,config,lang,io){
|
|||
})
|
||||
//unique Base64 socket stream
|
||||
cn.on('Base64',function(d){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
|
||||
cn.disconnect();return;
|
||||
}
|
||||
cn.ip=cn.request.connection.remoteAddress;
|
||||
|
@ -160,10 +139,10 @@ module.exports = function(s,config,lang,io){
|
|||
r=r[0];
|
||||
var Emitter,chunkChannel
|
||||
if(!d.channel){
|
||||
Emitter = s.group[d.ke].mon[d.id].emitter
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(d.channel)+config.pipeAddition
|
||||
}
|
||||
if(!Emitter){
|
||||
|
@ -217,7 +196,7 @@ module.exports = function(s,config,lang,io){
|
|||
})
|
||||
//unique FLV socket stream
|
||||
cn.on('FLV',function(d){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
|
||||
cn.disconnect();return;
|
||||
}
|
||||
cn.ip=cn.request.connection.remoteAddress;
|
||||
|
@ -233,10 +212,10 @@ module.exports = function(s,config,lang,io){
|
|||
r=r[0];
|
||||
var Emitter,chunkChannel
|
||||
if(!d.channel){
|
||||
Emitter = s.group[d.ke].mon[d.id].emitter
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(d.channel)+config.pipeAddition
|
||||
}
|
||||
if(!Emitter){
|
||||
|
@ -253,7 +232,7 @@ module.exports = function(s,config,lang,io){
|
|||
cn.closeSocketVideoStream = function(){
|
||||
Emitter.removeListener('data', contentWriter);
|
||||
}
|
||||
tx({time:toUTC(),buffer:s.group[d.ke].mon[d.id].firstStreamChunk[chunkChannel]})
|
||||
tx({time:toUTC(),buffer:s.group[d.ke].activeMonitors[d.id].firstStreamChunk[chunkChannel]})
|
||||
Emitter.on('data',contentWriter = function(buffer){
|
||||
tx({time:toUTC(),buffer:buffer})
|
||||
})
|
||||
|
@ -290,7 +269,7 @@ module.exports = function(s,config,lang,io){
|
|||
})
|
||||
//unique MP4 socket stream
|
||||
cn.on('MP4',function(d){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
|
||||
cn.disconnect();return;
|
||||
}
|
||||
cn.ip=cn.request.connection.remoteAddress;
|
||||
|
@ -306,10 +285,10 @@ module.exports = function(s,config,lang,io){
|
|||
r=r[0];
|
||||
var Emitter,chunkChannel
|
||||
if(!d.channel){
|
||||
Emitter = s.group[d.ke].mon[d.id].emitter
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(d.channel)+config.pipeAddition
|
||||
}
|
||||
if(!Emitter){
|
||||
|
@ -321,7 +300,7 @@ module.exports = function(s,config,lang,io){
|
|||
cn.auth=d.auth;
|
||||
cn.channel=d.channel;
|
||||
cn.socketVideoStream=d.id;
|
||||
var mp4frag = s.group[d.ke].mon[d.id].mp4frag[d.channel];
|
||||
var mp4frag = s.group[d.ke].activeMonitors[d.id].mp4frag[d.channel];
|
||||
var onInitialized = () => {
|
||||
cn.emit('mime', mp4frag.mime);
|
||||
mp4frag.removeListener('initialized', onInitialized);
|
||||
|
@ -438,13 +417,9 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
s.group[d.ke].users[d.auth].lang=s.getLanguageFile(s.group[d.ke].users[d.auth].details.lang)
|
||||
s.userLog({ke:d.ke,mid:'$USER'},{type:s.group[d.ke].users[d.auth].lang['Websocket Connected'],msg:{mail:r.mail,id:d.uid,ip:cn.ip}})
|
||||
if(!s.group[d.ke].mon){
|
||||
s.group[d.ke].mon={}
|
||||
if(!s.group[d.ke].mon){s.group[d.ke].mon={}}
|
||||
}
|
||||
if(s.isAtleatOneDetectorPluginConnected){
|
||||
s.sendDetectorInfoToClient({f:'detector_plugged'},tx)
|
||||
s.ocvTx({f:'readPlugins',ke:d.ke})
|
||||
if(!s.group[d.ke].activeMonitors){
|
||||
s.group[d.ke].activeMonitors={}
|
||||
if(!s.group[d.ke].activeMonitors){s.group[d.ke].activeMonitors={}}
|
||||
}
|
||||
tx({f:'users_online',users:s.group[d.ke].users})
|
||||
s.tx({f:'user_status_change',ke:d.ke,uid:cn.uid,status:1,user:s.group[d.ke].users[d.auth]},'GRP_'+d.ke)
|
||||
|
@ -465,7 +440,7 @@ module.exports = function(s,config,lang,io){
|
|||
s.sqlQuery('SELECT * FROM Monitors WHERE ke=?', [d.ke], function(err,r) {
|
||||
if(r && r[0]){
|
||||
r.forEach(function(monitor){
|
||||
s.cameraSendSnapshot({mid:monitor.mid,ke:monitor.ke,mon:monitor})
|
||||
s.cameraSendSnapshot({mid:monitor.mid,ke:monitor.ke,mon:monitor},{useIcon: true})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -474,7 +449,7 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
})
|
||||
s.onSocketAuthenticationExtensions.forEach(function(extender){
|
||||
extender(r,cn)
|
||||
extender(r,cn,d,tx)
|
||||
})
|
||||
}
|
||||
s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) {
|
||||
|
@ -507,9 +482,6 @@ module.exports = function(s,config,lang,io){
|
|||
if((d.id||d.uid||d.mid)&&cn.ke){
|
||||
try{
|
||||
switch(d.f){
|
||||
case'ocv_in':
|
||||
s.ocvTx(d.data)
|
||||
break;
|
||||
case'monitorOrder':
|
||||
if(d.monitorOrder && d.monitorOrder instanceof Object){
|
||||
s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){
|
||||
|
@ -744,9 +716,10 @@ module.exports = function(s,config,lang,io){
|
|||
getVideos(function(videos){
|
||||
getEvents(function(events){
|
||||
tx({
|
||||
f:'drawPowerVideoMainTimeLine',
|
||||
videos:videos,
|
||||
events:events
|
||||
f: 'videos&events',
|
||||
id: d.mid,
|
||||
videos: videos,
|
||||
events: events
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -781,7 +754,7 @@ module.exports = function(s,config,lang,io){
|
|||
case'watch_on':
|
||||
if(!d.ke){d.ke=cn.ke}
|
||||
s.initiateMonitorObject({mid:d.id,ke:d.ke});
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]||s.group[d.ke].mon[d.id].isStarted === false){return false}
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false}
|
||||
cn.join('MON_'+d.ke+d.id);
|
||||
cn.join('DETECTOR_'+d.ke+d.id);
|
||||
if(cn.jpeg_on !== true){
|
||||
|
@ -951,6 +924,7 @@ module.exports = function(s,config,lang,io){
|
|||
var tempSessionKey = s.gid(30)
|
||||
cn.superSessionKey = tempSessionKey
|
||||
s.superUsersApi[tempSessionKey] = data
|
||||
s.superUsersApi[tempSessionKey].cnid = cn.id
|
||||
if(!data.$user.tokens)data.$user.tokens = {}
|
||||
data.$user.tokens[tempSessionKey] = {}
|
||||
cn.ip=cn.request.connection.remoteAddress
|
||||
|
@ -1229,15 +1203,15 @@ module.exports = function(s,config,lang,io){
|
|||
login_type:'Streamer'
|
||||
}
|
||||
s.group[d.ke].dashcamUsers[d.auth] = s.group[d.ke].users[d.auth]
|
||||
if(s.group[d.ke].mon){
|
||||
Object.keys(s.group[d.ke].mon).forEach(function(monitorId){
|
||||
if(s.group[d.ke].activeMonitors){
|
||||
Object.keys(s.group[d.ke].activeMonitors).forEach(function(monitorId){
|
||||
var dataToClient = {
|
||||
f : 'disable_stream',
|
||||
mid : monitorId,
|
||||
ke : d.ke
|
||||
}
|
||||
var mon = s.group[d.ke].mon[monitorId]
|
||||
if(s.group[d.ke].mon_conf[monitorId].type === 'dashcam'){
|
||||
var mon = s.group[d.ke].activeMonitors[monitorId]
|
||||
if(s.group[d.ke].rawMonitorConfigurations[monitorId].type === 'dashcam'){
|
||||
if(mon.allowStdinWrite === true){
|
||||
dataToClient.f = 'enable_stream'
|
||||
}
|
||||
|
@ -1248,22 +1222,22 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
})
|
||||
}else{
|
||||
if(s.group[d.ke] && s.group[d.ke].mon[d.mid]){
|
||||
if(s.group[d.ke].mon[d.mid].allowStdinWrite === true){
|
||||
if(s.group[d.ke] && s.group[d.ke].activeMonitors[d.mid]){
|
||||
if(s.group[d.ke].activeMonitors[d.mid].allowStdinWrite === true){
|
||||
switch(d.f){
|
||||
case'monitor_chunk':
|
||||
if(s.group[d.ke].mon[d.mid].isStarted !== true || !s.group[d.ke].mon[d.mid].spawn || !s.group[d.ke].mon[d.mid].spawn.stdin){
|
||||
if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true || !s.group[d.ke].activeMonitors[d.mid].spawn || !s.group[d.ke].activeMonitors[d.mid].spawn.stdin){
|
||||
s.tx({error:'Not Started'},cn.id);
|
||||
return false
|
||||
};
|
||||
s.group[d.ke].mon[d.mid].spawn.stdin.write(new Buffer(d.chunk, "binary"));
|
||||
s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(new Buffer(d.chunk, "binary"));
|
||||
break;
|
||||
case'monitor_frame':
|
||||
if(s.group[d.ke].mon[d.mid].isStarted !== true){
|
||||
if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true){
|
||||
s.tx({error:'Not Started'},cn.id);
|
||||
return false
|
||||
};
|
||||
s.group[d.ke].mon[d.mid].spawn.stdin.write(d.frame);
|
||||
s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(d.frame);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
|
@ -1279,7 +1253,7 @@ module.exports = function(s,config,lang,io){
|
|||
tx=function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);}
|
||||
switch(d.f){
|
||||
case'init':
|
||||
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]||s.group[d.ke].mon[d.id].isStarted === false){return false}
|
||||
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false}
|
||||
s.auth({auth:d.auth,ke:d.ke,id:d.id,ip:cn.request.connection.remoteAddress},function(user){
|
||||
cn.embedded=1;
|
||||
cn.ke=d.ke;
|
||||
|
@ -1294,10 +1268,10 @@ module.exports = function(s,config,lang,io){
|
|||
cn.join('MON_STREAM_'+d.ke+d.id);
|
||||
cn.join('DETECTOR_'+d.ke+d.id);
|
||||
cn.join('STR_'+d.ke);
|
||||
if(s.group[d.ke]&&s.group[d.ke].mon[d.id]&&s.group[d.ke].mon[d.id].watch){
|
||||
if(s.group[d.ke]&&s.group[d.ke].activeMonitors[d.id]&&s.group[d.ke].activeMonitors[d.id].watch){
|
||||
|
||||
tx({f:'monitor_watch_on',id:d.id,ke:d.ke},'MON_'+d.ke+d.id)
|
||||
s.tx({viewers:Object.keys(s.group[d.ke].mon[d.id].watch).length,ke:d.ke,id:d.id},'MON_'+d.ke+d.id)
|
||||
s.tx({viewers:Object.keys(s.group[d.ke].activeMonitors[d.id].watch).length,ke:d.ke,id:d.id},'MON_'+d.ke+d.id)
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -1364,20 +1338,9 @@ module.exports = function(s,config,lang,io){
|
|||
if(s.group[cn.ke].dashcamUsers && s.group[cn.ke].dashcamUsers[cn.auth])delete(s.group[cn.ke].dashcamUsers[cn.auth]);
|
||||
}
|
||||
}
|
||||
if(cn.pluginEngine){
|
||||
s.connectedPlugins[cn.pluginEngine].plugged = false
|
||||
s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU')
|
||||
}
|
||||
if(cn.cron){
|
||||
delete(s.cron);
|
||||
}
|
||||
if(cn.detectorPlugin){
|
||||
s.tx({f:'detector_unplugged',plug:cn.detectorPlugin},'CPU')
|
||||
s.removeDetectorPlugin(cn.detectorPlugin)
|
||||
s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){
|
||||
s.tx(data,'CPU')
|
||||
})
|
||||
}
|
||||
if(cn.superSessionKey){
|
||||
delete(s.superUsersApi[cn.superSessionKey])
|
||||
}
|
||||
|
|
30
libs/sql.js
30
libs/sql.js
|
@ -1,3 +1,5 @@
|
|||
var fs = require('fs');
|
||||
var async = require("async");
|
||||
module.exports = function(s,config){
|
||||
s.onBeforeDatabaseLoadExtensions.forEach(function(extender){
|
||||
extender(config)
|
||||
|
@ -8,8 +10,8 @@ module.exports = function(s,config){
|
|||
connection: config.db,
|
||||
}
|
||||
if(s.databaseOptions.client.indexOf('sqlite')>-1){
|
||||
s.databaseOptions.client = 'sqlite3';
|
||||
s.databaseOptions.useNullAsDefault = true;
|
||||
s.databaseOptions.client = 'sqlite3'
|
||||
s.databaseOptions.useNullAsDefault = true
|
||||
try{
|
||||
require('sqlite3')
|
||||
}catch(err){
|
||||
|
@ -54,6 +56,11 @@ module.exports = function(s,config){
|
|||
newValue = new Date(value.replace('T',' '))
|
||||
return newValue
|
||||
}
|
||||
var runQuery = async.queue(function(data, callback) {
|
||||
s.databaseEngine
|
||||
.raw(data.query,data.values)
|
||||
.asCallback(callback)
|
||||
}, 4);
|
||||
s.sqlQuery = function(query,values,onMoveOn,hideLog){
|
||||
if(!values){values=[]}
|
||||
if(typeof values === 'function'){
|
||||
|
@ -66,14 +73,17 @@ module.exports = function(s,config){
|
|||
// .replace(/ NOT LIKE /g," NOT ILIKE ")
|
||||
// .replace(/ LIKE /g," ILIKE ")
|
||||
// }
|
||||
var mergedQuery = s.mergeQueryValues(query,values)
|
||||
s.debugLog('s.sqlQuery QUERY',mergedQuery)
|
||||
if(config.debugLog === true){
|
||||
var mergedQuery = s.mergeQueryValues(query,values)
|
||||
s.debugLog('s.sqlQuery QUERY',mergedQuery)
|
||||
}
|
||||
if(!s.databaseEngine || !s.databaseEngine.raw){
|
||||
s.connectDatabase()
|
||||
}
|
||||
return s.databaseEngine
|
||||
.raw(query,values)
|
||||
.asCallback(function(err,r){
|
||||
return runQuery.push({
|
||||
query: query,
|
||||
values: values
|
||||
},function(err,r){
|
||||
if(err && !hideLog){
|
||||
console.log('s.sqlQuery QUERY ERRORED',query)
|
||||
console.log('s.sqlQuery ERROR',err)
|
||||
|
@ -129,6 +139,10 @@ module.exports = function(s,config){
|
|||
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\',`details` text)' + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
},true)
|
||||
//add Cloud Timelapse Frames table, will remove in future
|
||||
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Timelapse Frames` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`href` text NOT NULL,`details` longtext,`filename` varchar(50) NOT NULL,`time` timestamp NULL DEFAULT NULL,`size` int(11) NOT NULL)' + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
},true)
|
||||
//create Files table
|
||||
var createFilesTableQuery = "CREATE TABLE IF NOT EXISTS `Files` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`name` tinytext NOT NULL,`size` float NOT NULL DEFAULT '0',`details` text NOT NULL,`status` int(1) NOT NULL DEFAULT '0',`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP)"
|
||||
s.sqlQuery(createFilesTableQuery + mySQLtail + ';',[],function(err){
|
||||
|
@ -140,7 +154,7 @@ module.exports = function(s,config){
|
|||
aQuery += "INSERT INTO Files (`ke`, `mid`, `name`, `details`, `size`, `status`, `time`) SELECT `ke`, `mid`, `name`, `details`, `size`, `status`, `time` FROM _Files_old;COMMIT;DROP TABLE _Files_old;"
|
||||
}else{
|
||||
s.sqlQuery('ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;',[],function(err){
|
||||
if(err && err.sqlMessage.indexOf('Duplicate') === -1)console.error(err)
|
||||
if(err && err.sqlMessage && err.sqlMessage.indexOf('Duplicate') === -1)console.error(err)
|
||||
},true)
|
||||
}
|
||||
},true)
|
||||
|
|
300
libs/startup.js
300
libs/startup.js
|
@ -1,12 +1,13 @@
|
|||
|
||||
var fs = require('fs');
|
||||
var request = require('request');
|
||||
var moment = require('moment');
|
||||
var crypto = require('crypto');
|
||||
var exec = require('child_process').exec;
|
||||
var execSync = require('child_process').execSync;
|
||||
module.exports = function(s,config,lang,io){
|
||||
console.log('FFmpeg version : '+s.ffmpegVersion)
|
||||
console.log('Node.js version : '+execSync("node -v"))
|
||||
console.log('Node.js version : '+process.version)
|
||||
s.processReady = function(){
|
||||
s.systemLog(lang.startUpText5)
|
||||
s.onProcessReadyExtensions.forEach(function(extender){
|
||||
|
@ -18,7 +19,7 @@ module.exports = function(s,config,lang,io){
|
|||
var next = function(){
|
||||
if(callback)callback()
|
||||
}
|
||||
if(!s.isWin){
|
||||
if(!s.isWin && s.packageJson.mainDirectory !== '.'){
|
||||
var etcPath = '/etc/shinobisystems/cctv.txt'
|
||||
fs.stat(etcPath,function(err,stat){
|
||||
if(err || !stat){
|
||||
|
@ -33,6 +34,7 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
}
|
||||
var loadedAccounts = []
|
||||
var foundMonitors = []
|
||||
var loadMonitors = function(callback){
|
||||
s.beforeMonitorsLoadedOnStartupExtensions.forEach(function(extender){
|
||||
extender()
|
||||
|
@ -40,19 +42,17 @@ module.exports = function(s,config,lang,io){
|
|||
s.systemLog(lang.startUpText4)
|
||||
//preliminary monitor start
|
||||
s.sqlQuery('SELECT * FROM Monitors', function(err,monitors) {
|
||||
foundMonitors = monitors
|
||||
if(err){s.systemLog(err)}
|
||||
if(monitors && monitors[0]){
|
||||
var loadCompleted = 0
|
||||
var orphanedVideosForMonitors = {}
|
||||
var loadMonitor = function(monitor){
|
||||
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
|
||||
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
|
||||
s.initiateMonitorObject(monitor)
|
||||
s.orphanedVideoCheck(monitor,2,function(orphanedFilesCount){
|
||||
if(orphanedFilesCount){
|
||||
orphanedVideosForMonitors[monitor.ke][monitor.mid] += orphanedFilesCount
|
||||
}
|
||||
s.group[monitor.ke].mon_conf[monitor.mid] = monitor
|
||||
setTimeout(function(){
|
||||
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
|
||||
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
|
||||
s.initiateMonitorObject(monitor)
|
||||
s.group[monitor.ke].rawMonitorConfigurations[monitor.mid] = monitor
|
||||
s.sendMonitorStatus({id:monitor.mid,ke:monitor.ke,status:'Stopped'});
|
||||
var monObj = Object.assign(monitor,{id : monitor.mid})
|
||||
s.camera(monitor.mode,monObj)
|
||||
|
@ -60,10 +60,9 @@ module.exports = function(s,config,lang,io){
|
|||
if(monitors[loadCompleted]){
|
||||
loadMonitor(monitors[loadCompleted])
|
||||
}else{
|
||||
s.systemLog(lang.startUpText6+' : '+s.s(orphanedVideosForMonitors))
|
||||
callback()
|
||||
}
|
||||
})
|
||||
},2000)
|
||||
}
|
||||
loadMonitor(monitors[loadCompleted])
|
||||
}else{
|
||||
|
@ -71,19 +70,89 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
})
|
||||
}
|
||||
var checkForOrphanedVideos = function(callback){
|
||||
var monitors = foundMonitors
|
||||
if(monitors && monitors[0]){
|
||||
var loadCompleted = 0
|
||||
var orphanedVideosForMonitors = {}
|
||||
var checkForOrphanedVideosForMonitor = function(monitor){
|
||||
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
|
||||
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
|
||||
s.orphanedVideoCheck(monitor,null,function(orphanedFilesCount){
|
||||
if(orphanedFilesCount){
|
||||
orphanedVideosForMonitors[monitor.ke][monitor.mid] += orphanedFilesCount
|
||||
}
|
||||
++loadCompleted
|
||||
if(monitors[loadCompleted]){
|
||||
checkForOrphanedVideosForMonitor(monitors[loadCompleted])
|
||||
}else{
|
||||
s.systemLog(lang.startUpText6+' : '+s.s(orphanedVideosForMonitors))
|
||||
delete(foundMonitors)
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
checkForOrphanedVideosForMonitor(monitors[loadCompleted])
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
var loadDiskUseForUser = function(user,callback){
|
||||
s.systemLog(user.mail+' : '+lang.startUpText0)
|
||||
var userDetails = JSON.parse(user.details)
|
||||
user.size = 0
|
||||
user.limit = userDetails.size
|
||||
s.group[user.ke].sizeLimit = parseFloat(userDetails.size) || 10000
|
||||
s.group[user.ke].sizeLimitVideoPercent = parseFloat(userDetails.size_video_percent) || 90
|
||||
s.group[user.ke].sizeLimitTimelapseFramesPercent = parseFloat(userDetails.size_timelapse_percent) || 10
|
||||
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
|
||||
if(videos && videos[0]){
|
||||
videos.forEach(function(video){
|
||||
user.size += video.size
|
||||
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE ke=?',[user.ke],function(err,timelapseFrames){
|
||||
s.sqlQuery('SELECT * FROM `Files` WHERE ke=?',[user.ke],function(err,files){
|
||||
var usedSpaceVideos = 0
|
||||
var usedSpaceTimelapseFrames = 0
|
||||
var usedSpaceFilebin = 0
|
||||
var addStorageData = {
|
||||
files: [],
|
||||
videos: [],
|
||||
timelapeFrames: [],
|
||||
}
|
||||
if(videos && videos[0]){
|
||||
videos.forEach(function(video){
|
||||
video.details = s.parseJSON(video.details)
|
||||
if(!video.details.dir){
|
||||
usedSpaceVideos += video.size
|
||||
}else{
|
||||
addStorageData.videos.push(video)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(timelapseFrames && timelapseFrames[0]){
|
||||
timelapseFrames.forEach(function(frame){
|
||||
frame.details = s.parseJSON(frame.details)
|
||||
if(!frame.details.dir){
|
||||
usedSpaceTimelapseFrames += frame.size
|
||||
}else{
|
||||
addStorageData.timelapeFrames.push(frame)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(files && files[0]){
|
||||
files.forEach(function(file){
|
||||
file.details = s.parseJSON(file.details)
|
||||
if(!file.details.dir){
|
||||
usedSpaceFilebin += file.size
|
||||
}else{
|
||||
addStorageData.files.push(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
s.group[user.ke].usedSpace = (usedSpaceVideos + usedSpaceTimelapseFrames + usedSpaceFilebin) / 1000000
|
||||
s.group[user.ke].usedSpaceVideos = usedSpaceVideos / 1000000
|
||||
s.group[user.ke].usedSpaceFilebin = usedSpaceFilebin / 1000000
|
||||
s.group[user.ke].usedSpaceTimelapseFrames = usedSpaceTimelapseFrames / 1000000
|
||||
loadAddStorageDiskUseForUser(user,addStorageData,function(){
|
||||
callback()
|
||||
})
|
||||
})
|
||||
}
|
||||
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+videos.length,user.size)
|
||||
callback()
|
||||
})
|
||||
})
|
||||
}
|
||||
var loadCloudDiskUseForUser = function(user,callback){
|
||||
|
@ -98,23 +167,108 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
if(s.cloudDiskUseStartupExtensions[storageType])s.cloudDiskUseStartupExtensions[storageType](user,userDetails)
|
||||
})
|
||||
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
|
||||
var loadCloudVideos = function(callback){
|
||||
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
|
||||
if(videos && videos[0]){
|
||||
videos.forEach(function(video){
|
||||
var storageType = JSON.parse(video.details).type
|
||||
if(!storageType)storageType = 's3'
|
||||
var videoSize = video.size / 1000000
|
||||
user.cloudDiskUse[storageType].usedSpace += videoSize
|
||||
user.cloudDiskUse[storageType].usedSpaceVideos += videoSize
|
||||
++user.cloudDiskUse[storageType].firstCount
|
||||
})
|
||||
s.cloudDisksLoaded.forEach(function(storageType){
|
||||
var firstCount = user.cloudDiskUse[storageType].firstCount
|
||||
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+firstCount,storageType,user.cloudDiskUse[storageType].usedSpace)
|
||||
delete(user.cloudDiskUse[storageType].firstCount)
|
||||
})
|
||||
}
|
||||
callback()
|
||||
})
|
||||
}
|
||||
var loadCloudTimelapseFrames = function(callback){
|
||||
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE ke=?',[user.ke],function(err,frames){
|
||||
if(frames && frames[0]){
|
||||
frames.forEach(function(frame){
|
||||
var storageType = JSON.parse(frame.details).type
|
||||
if(!storageType)storageType = 's3'
|
||||
var frameSize = frame.size / 1000000
|
||||
user.cloudDiskUse[storageType].usedSpace += frameSize
|
||||
user.cloudDiskUse[storageType].usedSpaceTimelapseFrames += frameSize
|
||||
})
|
||||
}
|
||||
callback()
|
||||
})
|
||||
}
|
||||
loadCloudVideos(function(){
|
||||
loadCloudTimelapseFrames(function(){
|
||||
s.group[user.ke].cloudDiskUse = user.cloudDiskUse
|
||||
callback()
|
||||
})
|
||||
})
|
||||
}
|
||||
var loadAddStorageDiskUseForUser = function(user,data,callback){
|
||||
var videos = data.videos
|
||||
var timelapseFrames = data.timelapseFrames
|
||||
var files = data.files
|
||||
var userDetails = JSON.parse(user.details)
|
||||
var userAddStorageData = s.parseJSON(userDetails.addStorage) || {}
|
||||
var currentStorageNumber = 0
|
||||
var readStorageArray = function(){
|
||||
var storage = s.listOfStorage[currentStorageNumber]
|
||||
if(!storage){
|
||||
//done all checks, move on to next user
|
||||
callback()
|
||||
return
|
||||
}
|
||||
var path = storage.value
|
||||
if(path === ''){
|
||||
++currentStorageNumber
|
||||
readStorageArray()
|
||||
return
|
||||
}
|
||||
var storageId = path
|
||||
var storageData = userAddStorageData[storageId] || {}
|
||||
if(!s.group[user.ke].addStorageUse[storageId])s.group[user.ke].addStorageUse[storageId] = {}
|
||||
var storageIndex = s.group[user.ke].addStorageUse[storageId]
|
||||
storageIndex.name = storage.name
|
||||
storageIndex.path = path
|
||||
storageIndex.usedSpace = 0
|
||||
storageIndex.sizeLimit = parseFloat(storageData.limit) || parseFloat(userDetails.size) || 10000
|
||||
var usedSpaceVideos = 0
|
||||
var usedSpaceTimelapseFrames = 0
|
||||
var usedSpaceFilebin = 0
|
||||
if(videos && videos[0]){
|
||||
videos.forEach(function(video){
|
||||
var storageType = JSON.parse(video.details).type
|
||||
if(!storageType)storageType = 's3'
|
||||
user.cloudDiskUse[storageType].usedSpace += (video.size /1000000)
|
||||
++user.cloudDiskUse[storageType].firstCount
|
||||
})
|
||||
s.cloudDisksLoaded.forEach(function(storageType){
|
||||
var firstCount = user.cloudDiskUse[storageType].firstCount
|
||||
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+firstCount,storageType,user.cloudDiskUse[storageType].usedSpace)
|
||||
delete(user.cloudDiskUse[storageType].firstCount)
|
||||
if(video.details.dir === storage.value){
|
||||
usedSpaceVideos += video.size
|
||||
}
|
||||
})
|
||||
}
|
||||
s.group[user.ke].cloudDiskUse = user.cloudDiskUse
|
||||
callback()
|
||||
})
|
||||
if(timelapseFrames && timelapseFrames[0]){
|
||||
timelapseFrames.forEach(function(frame){
|
||||
if(video.details.dir === storage.value){
|
||||
usedSpaceTimelapseFrames += frame.size
|
||||
}
|
||||
})
|
||||
}
|
||||
if(files && files[0]){
|
||||
files.forEach(function(file){
|
||||
if(video.details.dir === storage.value){
|
||||
usedSpaceFilebin += file.size
|
||||
}
|
||||
})
|
||||
}
|
||||
storageIndex.usedSpace = (usedSpaceVideos + usedSpaceTimelapseFrames + usedSpaceFilebin) / 1000000
|
||||
storageIndex.usedSpaceVideos = usedSpaceVideos / 1000000
|
||||
storageIndex.usedSpaceFilebin = usedSpaceFilebin / 1000000
|
||||
storageIndex.usedSpaceTimelapseFrames = usedSpaceTimelapseFrames / 1000000
|
||||
s.systemLog(user.mail+' : '+path+' : '+videos.length,storageIndex.usedSpace)
|
||||
++currentStorageNumber
|
||||
readStorageArray()
|
||||
}
|
||||
readStorageArray()
|
||||
}
|
||||
var loadAdminUsers = function(callback){
|
||||
//get current disk used for each isolated account (admin user) on startup
|
||||
|
@ -124,10 +278,10 @@ module.exports = function(s,config,lang,io){
|
|||
var count = users.length
|
||||
var countFinished = 0
|
||||
users.forEach(function(user){
|
||||
s.loadGroup(user)
|
||||
s.loadGroupApps(user)
|
||||
loadedAccounts.push(user.ke)
|
||||
loadDiskUseForUser(user,function(){
|
||||
s.loadGroup(user)
|
||||
s.loadGroupApps(user)
|
||||
++countFinished
|
||||
if(countFinished === count){
|
||||
callback()
|
||||
|
@ -157,36 +311,76 @@ module.exports = function(s,config,lang,io){
|
|||
}
|
||||
})
|
||||
}
|
||||
config.userHasSubscribed = false
|
||||
var checkSubscription = function(callback){
|
||||
var subscriptionFailed = function(){
|
||||
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
console.error('This Install of Shinobi is NOT Activated')
|
||||
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
s.systemLog('This Install of Shinobi is NOT Activated')
|
||||
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
console.log('https://licenses.shinobi.video/subscribe')
|
||||
}
|
||||
if(config.subscriptionId){
|
||||
var url = 'https://licenses.shinobi.video/subscribe/check?subscriptionId=' + config.subscriptionId
|
||||
request(url,{
|
||||
method: 'GET',
|
||||
timeout: 30000
|
||||
}, function(err,resp,body){
|
||||
var json = s.parseJSON(body)
|
||||
if(err)console.log(err,json)
|
||||
var hasSubcribed = !!json.ok
|
||||
config.userHasSubscribed = hasSubcribed
|
||||
callback(hasSubcribed)
|
||||
if(config.userHasSubscribed){
|
||||
s.systemLog('This Install of Shinobi is Activated')
|
||||
}else{
|
||||
subscriptionFailed()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
subscriptionFailed()
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
//check disk space every 20 minutes
|
||||
if(config.autoDropCache===true){
|
||||
setInterval(function(){
|
||||
exec('echo 3 > /proc/sys/vm/drop_caches',{detached: true})
|
||||
},60000*20)
|
||||
}
|
||||
//master node - startup functions
|
||||
setInterval(function(){
|
||||
s.cpuUsage(function(cpu){
|
||||
s.ramUsage(function(ram){
|
||||
s.tx({f:'os',cpu:cpu,ram:ram},'CPU');
|
||||
})
|
||||
})
|
||||
},10000)
|
||||
//hourly check to see if sizePurge has failed to unlock
|
||||
//checks to see if request count is the number of monitors + 10
|
||||
s.checkForStalePurgeLocks()
|
||||
//run prerequsite queries, load users and monitors
|
||||
if(config.childNodes.mode !== 'child'){
|
||||
//master node - startup functions
|
||||
setInterval(function(){
|
||||
s.cpuUsage(function(cpu){
|
||||
s.ramUsage(function(ram){
|
||||
s.tx({f:'os',cpu:cpu,ram:ram},'CPU');
|
||||
})
|
||||
})
|
||||
},10000)
|
||||
//hourly check to see if sizePurge has failed to unlock
|
||||
//checks to see if request count is the number of monitors + 10
|
||||
s.checkForStalePurgeLocks()
|
||||
//run prerequsite queries, load users and monitors
|
||||
//sql/database connection with knex
|
||||
s.databaseEngine = require('knex')(s.databaseOptions)
|
||||
//run prerequsite queries
|
||||
s.preQueries()
|
||||
setTimeout(function(){
|
||||
checkForTerminalCommands(function(){
|
||||
//load administrators (groups)
|
||||
loadAdminUsers(function(){
|
||||
//load monitors (for groups)
|
||||
loadMonitors(function(){
|
||||
s.processReady()
|
||||
//check for subscription
|
||||
checkSubscription(function(){
|
||||
//check terminal commander
|
||||
checkForTerminalCommands(function(){
|
||||
//load administrators (groups)
|
||||
loadAdminUsers(function(){
|
||||
//load monitors (for groups)
|
||||
loadMonitors(function(){
|
||||
//check for orphaned videos
|
||||
checkForOrphanedVideos(function(){
|
||||
s.processReady()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
368
libs/timelapse.js
Normal file
368
libs/timelapse.js
Normal file
|
@ -0,0 +1,368 @@
|
|||
var fs = require('fs')
|
||||
var moment = require('moment')
|
||||
var express = require('express')
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
s.getTimelapseFrameDirectory = function(e){
|
||||
if(e.mid&&!e.id){e.id=e.mid}
|
||||
s.checkDetails(e)
|
||||
if(e.details&&e.details.dir&&e.details.dir!==''){
|
||||
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'_timelapse/'
|
||||
}else{
|
||||
return s.dir.videos+e.ke+'/'+e.id+'_timelapse/';
|
||||
}
|
||||
}
|
||||
s.createTimelapseFrameAndInsert = function(e,location,filename){
|
||||
//e = monitor object
|
||||
//location = file location
|
||||
var filePath = location + filename
|
||||
var fileStats = fs.statSync(filePath)
|
||||
var details = {}
|
||||
if(e.details && e.details.dir && e.details.dir !== ''){
|
||||
details.dir = e.details.dir
|
||||
}
|
||||
var timeNow = new Date()
|
||||
var queryInfo = {
|
||||
ke: e.ke,
|
||||
mid: e.id,
|
||||
details: s.s(details),
|
||||
filename: filename,
|
||||
size: fileStats.size,
|
||||
time: timeNow
|
||||
}
|
||||
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
|
||||
var currentDate = s.formattedTime(queryInfo.time,'YYYY-MM-DD')
|
||||
s.cx({
|
||||
f: 'open_timelapse_file_transfer',
|
||||
ke: e.ke,
|
||||
mid: e.id,
|
||||
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
||||
filename: filename,
|
||||
currentDate: currentDate,
|
||||
queryInfo: queryInfo
|
||||
})
|
||||
var formattedTime = s.timeObject(timeNow).format()
|
||||
fs.createReadStream(filePath,{ highWaterMark: 500 })
|
||||
.on('data',function(data){
|
||||
s.cx({
|
||||
f: 'created_timelapse_file_chunk',
|
||||
ke: e.ke,
|
||||
mid: e.id,
|
||||
time: formattedTime,
|
||||
filesize: e.filesize,
|
||||
chunk: data,
|
||||
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
||||
filename: filename,
|
||||
currentDate: currentDate,
|
||||
queryInfo: queryInfo
|
||||
})
|
||||
})
|
||||
.on('close',function(){
|
||||
s.cx({
|
||||
f: 'created_timelapse_file',
|
||||
ke: e.ke,
|
||||
mid: e.id,
|
||||
time: formattedTime,
|
||||
filesize: e.filesize,
|
||||
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
||||
filename: filename,
|
||||
currentDate: currentDate,
|
||||
queryInfo: queryInfo
|
||||
})
|
||||
})
|
||||
}else{
|
||||
s.insertTimelapseFrameDatabaseRow(e,queryInfo,filePath)
|
||||
}
|
||||
}
|
||||
s.insertTimelapseFrameDatabaseRow = function(e,queryInfo,filePath){
|
||||
s.sqlQuery('INSERT INTO `Timelapse Frames` ('+Object.keys(queryInfo).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(queryInfo))
|
||||
s.setDiskUsedForGroup(e,queryInfo.size / 1000000,'timelapeFrames')
|
||||
s.purgeDiskForGroup(e)
|
||||
s.onInsertTimelapseFrameExtensions.forEach(function(extender){
|
||||
extender(e,queryInfo,filePath)
|
||||
})
|
||||
}
|
||||
s.onDeleteTimelapseFrameFromCloudExtensions = {}
|
||||
s.onDeleteTimelapseFrameFromCloudExtensionsRunner = function(e,storageType,video){
|
||||
// e = user
|
||||
if(!storageType){
|
||||
var videoDetails = JSON.parse(r.details)
|
||||
videoDetails.type = videoDetails.type || 's3'
|
||||
}
|
||||
if(s.onDeleteTimelapseFrameFromCloudExtensions[storageType]){
|
||||
s.onDeleteTimelapseFrameFromCloudExtensions[storageType](e,video,function(){
|
||||
s.tx({
|
||||
f: 'timelapse_frame_delete_cloud',
|
||||
mid: e.mid,
|
||||
ke: e.ke,
|
||||
time: e.time,
|
||||
end: e.end
|
||||
},'GRP_'+e.ke);
|
||||
})
|
||||
}
|
||||
}
|
||||
s.deleteTimelapseFrameFromCloud = function(e){
|
||||
// e = video object
|
||||
s.checkDetails(e)
|
||||
var frameSelector = [e.id,e.ke,new Date(e.time)]
|
||||
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(err,r){
|
||||
if(r&&r[0]){
|
||||
r = r[0]
|
||||
s.sqlQuery('DELETE FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(){
|
||||
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,r)
|
||||
})
|
||||
}else{
|
||||
// console.log('Delete Failed',e)
|
||||
// console.error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
// Web Paths
|
||||
// // // // //
|
||||
/**
|
||||
* API : Get Timelapse images
|
||||
*/
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke',
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id',
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date',
|
||||
], function (req,res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
|
||||
if(
|
||||
user.permissions.watch_videos==="0" ||
|
||||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
|
||||
){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
req.sql='SELECT * FROM `Timelapse Frames` WHERE ke=?';req.ar=[req.params.ke];
|
||||
if(req.query.archived=='1'){
|
||||
req.sql+=' AND details LIKE \'%"archived":"1"\''
|
||||
}
|
||||
if(!req.params.id){
|
||||
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
|
||||
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
|
||||
req.or=[];
|
||||
user.details.monitors.forEach(function(v,n){
|
||||
req.or.push('mid=?');req.ar.push(v)
|
||||
})
|
||||
req.sql+=' AND ('+req.or.join(' OR ')+')'
|
||||
}
|
||||
}else{
|
||||
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
|
||||
req.sql+=' and mid=?'
|
||||
req.ar.push(req.params.id)
|
||||
}else{
|
||||
res.end('[]');
|
||||
return;
|
||||
}
|
||||
}
|
||||
var isMp4Call = false
|
||||
if(req.query.mp4){
|
||||
isMp4Call = true
|
||||
}
|
||||
if(req.params.date){
|
||||
if(req.params.date.indexOf('-') === -1 && !isNaN(req.params.date)){
|
||||
req.params.date = parseInt(req.params.date)
|
||||
}
|
||||
var selectedDate = req.params.date
|
||||
if(typeof req.params.date === 'string' && req.params.date.indexOf('.') > -1){
|
||||
isMp4Call = true
|
||||
selectedDate = req.params.date.split('.')[0]
|
||||
}
|
||||
selectedDate = new Date(selectedDate)
|
||||
var utcSelectedDate = new Date(selectedDate.getTime() + selectedDate.getTimezoneOffset() * 60000)
|
||||
req.query.start = moment(utcSelectedDate).format('YYYY-MM-DD HH:mm:ss')
|
||||
var dayAfter = utcSelectedDate
|
||||
dayAfter.setDate(dayAfter.getDate() + 1)
|
||||
req.query.end = moment(dayAfter).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
if(req.query.start||req.query.end){
|
||||
if(!req.query.startOperator||req.query.startOperator==''){
|
||||
req.query.startOperator='>='
|
||||
}
|
||||
if(!req.query.endOperator||req.query.endOperator==''){
|
||||
req.query.endOperator='<='
|
||||
}
|
||||
if(req.query.start && req.query.start !== '' && req.query.end && req.query.end !== ''){
|
||||
req.query.start = s.stringToSqlTime(req.query.start)
|
||||
req.query.end = s.stringToSqlTime(req.query.end)
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `time` '+req.query.endOperator+' ?';
|
||||
req.ar.push(req.query.start)
|
||||
req.ar.push(req.query.end)
|
||||
}else if(req.query.start && req.query.start !== ''){
|
||||
req.query.start = s.stringToSqlTime(req.query.start)
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ?';
|
||||
req.ar.push(req.query.start)
|
||||
}
|
||||
}
|
||||
// if(!req.query.limit||req.query.limit==''){req.query.limit=288}
|
||||
req.sql+=' ORDER BY `time` DESC'
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
if(isMp4Call){
|
||||
if(r && r[0]){
|
||||
s.createVideoFromTimelapse(r,req.query.fps,function(response){
|
||||
if(response.fileExists){
|
||||
if(req.query.download){
|
||||
res.setHeader('Content-Type', 'video/mp4')
|
||||
s.streamMp4FileOverHttp(response.fileLocation,req,res)
|
||||
}else{
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end(s.prettyPrint({
|
||||
ok : response.ok,
|
||||
fileExists : response.fileExists,
|
||||
msg : response.msg,
|
||||
}))
|
||||
}
|
||||
}else{
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end(s.prettyPrint({
|
||||
ok : response.ok,
|
||||
fileExists : response.fileExists,
|
||||
msg : response.msg,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}else{
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(s.prettyPrint([]))
|
||||
}
|
||||
}else{
|
||||
if(r && r[0]){
|
||||
r.forEach(function(file){
|
||||
file.details = s.parseJSON(file.details)
|
||||
})
|
||||
res.end(s.prettyPrint(r))
|
||||
}else{
|
||||
res.end(s.prettyPrint([]))
|
||||
}
|
||||
}
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Timelapse images
|
||||
*/
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date/:filename',
|
||||
], function (req,res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
|
||||
if(
|
||||
user.permissions.watch_videos==="0" ||
|
||||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
|
||||
){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
req.sql='SELECT * FROM `Timelapse Frames` WHERE ke=?';req.ar=[req.params.ke];
|
||||
if(req.query.archived=='1'){
|
||||
req.sql+=' AND details LIKE \'%"archived":"1"\''
|
||||
}
|
||||
if(!req.params.id){
|
||||
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
|
||||
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
|
||||
req.or=[];
|
||||
user.details.monitors.forEach(function(v,n){
|
||||
req.or.push('mid=?');req.ar.push(v)
|
||||
})
|
||||
req.sql+=' AND ('+req.or.join(' OR ')+')'
|
||||
}
|
||||
}else{
|
||||
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
|
||||
req.sql+=' and mid=?'
|
||||
req.ar.push(req.params.id)
|
||||
}else{
|
||||
res.end('[]');
|
||||
return;
|
||||
}
|
||||
}
|
||||
req.sql+=' AND filename=?'
|
||||
req.ar.push(req.params.filename)
|
||||
req.sql+=' ORDER BY `time` DESC'
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
if(r && r[0]){
|
||||
var frame = r[0]
|
||||
frame.details = s.parseJSON(frame.details)
|
||||
var fileLocation
|
||||
if(frame.details.dir){
|
||||
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
|
||||
}else{
|
||||
fileLocation = `${s.dir.videos}`
|
||||
}
|
||||
var selectedDate = req.params.date
|
||||
if(selectedDate.indexOf('-') === -1){
|
||||
selectedDate = req.params.filename.split('T')[0]
|
||||
}
|
||||
fileLocation = `${fileLocation}${frame.ke}/${frame.mid}_timelapse/${selectedDate}/${req.params.filename}`
|
||||
fs.stat(fileLocation,function(err,stats){
|
||||
if(!err){
|
||||
res.contentType('image/jpeg')
|
||||
res.on('finish',function(){res.end()})
|
||||
fs.createReadStream(fileLocation).pipe(res)
|
||||
}else{
|
||||
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
|
||||
}
|
||||
})
|
||||
}else{
|
||||
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
|
||||
}
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* Page : Get Timelapse Page (Not Modal)
|
||||
*/
|
||||
app.get(config.webPaths.apiPrefix+':auth/timelapsePage/:ke', function (req,res){
|
||||
req.params.protocol=req.protocol;
|
||||
s.auth(req.params,function(user){
|
||||
// if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
|
||||
// res.end(user.lang['Not Permitted'])
|
||||
// return
|
||||
// }
|
||||
req.params.uid = user.uid
|
||||
s.renderPage(req,res,config.renderPaths.timelapse,{
|
||||
$user: user,
|
||||
data: req.params,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
lang: user.lang,
|
||||
originalURL: s.getOriginalUrl(req)
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
var buildTimelapseVideos = function(){
|
||||
var dateNow = new Date()
|
||||
var hoursNow = dateNow.getHours()
|
||||
if(hoursNow === 1){
|
||||
var dateNowMoment = moment(dateNow).utc().format('YYYY-MM-DDTHH:mm:ss')
|
||||
var dateMinusOneDay = moment(dateNow).utc().subtract(1, 'days').format('YYYY-MM-DDTHH:mm:ss')
|
||||
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE time => ? AND time =< ?',[dateMinusOneDay,dateNowMoment],function(err,frames){
|
||||
console.log(frames.length)
|
||||
var groups = {}
|
||||
frames.forEach(function(frame){
|
||||
if(groups[frame.ke])groups[frame.ke] = {}
|
||||
if(groups[frame.ke][frame.mid])groups[frame.ke][frame.mid] = []
|
||||
groups[frame.ke][frame.mid].push(frame)
|
||||
})
|
||||
Object.keys(groups).forEach(function(groupKey){
|
||||
Object.keys(groups[groupKey]).forEach(function(monitorId){
|
||||
var frameSet = groups[groupKey][monitorId]
|
||||
s.createVideoFromTimelapse(frameSet,30,function(response){
|
||||
if(response.ok){
|
||||
|
||||
}
|
||||
console.log(response.fileLocation)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
// Auto Build Timelapse Videos
|
||||
if(config.autoBuildTimelapseVideosDaily === true){
|
||||
setInterval(buildTimelapseVideos,1000 * 60 * 60 * 0.75)//every 45 minutes
|
||||
buildTimelapseVideos()
|
||||
}
|
||||
}
|
|
@ -1,20 +1,21 @@
|
|||
module.exports = function(s,config,lang){
|
||||
config.uploaderEjsBlocks = []
|
||||
s.uploaderFields = []
|
||||
var loadLib = function(lib){
|
||||
var uploadersFolder = __dirname + '/uploaders/'
|
||||
var libraryPath = uploadersFolder + lib + '.js'
|
||||
var loadedLib = require(libraryPath)(s,config,lang)
|
||||
if(lib !== 'loader'){
|
||||
var libraryEjsFile = uploadersFolder + lib + '.ejs'
|
||||
config.uploaderEjsBlocks.push(libraryEjsFile)
|
||||
loadedLib.isFormGroupGroup = true
|
||||
s.uploaderFields.push(loadedLib)
|
||||
}
|
||||
return require(libraryPath)
|
||||
return loadedLib
|
||||
}
|
||||
loadLib('loader')(s,config,lang)
|
||||
loadLib('loader')
|
||||
//cloud storage
|
||||
loadLib('s3based')(s,config,lang)
|
||||
loadLib('backblazeB2')(s,config,lang)
|
||||
loadLib('amazonS3')(s,config,lang)
|
||||
loadLib('webdav')(s,config,lang)
|
||||
loadLib('s3based')
|
||||
loadLib('backblazeB2')
|
||||
loadLib('amazonS3')
|
||||
loadLib('webdav')
|
||||
//simple storage
|
||||
loadLib('sftp')(s,config,lang)
|
||||
loadLib('sftp')
|
||||
}
|
||||
|
|
|
@ -123,6 +123,67 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
}
|
||||
}
|
||||
var onInsertTimelapseFrame = function(monitorObject,queryInfo,filePath){
|
||||
var e = monitorObject
|
||||
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 fileStream = fs.createReadStream(filePath)
|
||||
fileStream.on('error', function (err) {
|
||||
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({
|
||||
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})
|
||||
}
|
||||
if(s.group[e.ke].init.aws_s3_log === '1' && data && data.Location){
|
||||
var save = [
|
||||
queryInfo.mid,
|
||||
queryInfo.ke,
|
||||
queryInfo.time,
|
||||
s.s({
|
||||
type : 's3',
|
||||
location : saveLocation,
|
||||
}),
|
||||
queryInfo.size,
|
||||
data.Location
|
||||
]
|
||||
s.sqlQuery('INSERT INTO `Cloud Timelapse Frames` (mid,ke,time,details,size,href) VALUES (?,?,?,?,?,?)',save)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
amount : s.kilobyteToMegabyte(queryInfo.size),
|
||||
storageType : 's3'
|
||||
},'timelapseFrames')
|
||||
s.purgeCloudDiskForGroup(e,'s3','timelapseFrames')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
|
||||
// e = user
|
||||
try{
|
||||
var frameDetails = JSON.parse(frame.details)
|
||||
}catch(err){
|
||||
var frameDetails = frame.details
|
||||
}
|
||||
if(frameDetails.type !== 's3'){
|
||||
return
|
||||
}
|
||||
if(!frameDetails.location){
|
||||
frameDetails.location = frame.href.split(locationUrl)[1]
|
||||
}
|
||||
s.group[e.ke].aws_s3.deleteObject({
|
||||
Bucket: s.group[e.ke].init.aws_s3_bucket,
|
||||
Key: frameDetails.location,
|
||||
}, function(err, data) {
|
||||
if (err) console.log(err);
|
||||
callback()
|
||||
});
|
||||
}
|
||||
//amazon s3
|
||||
s.addCloudUploader({
|
||||
name: 's3',
|
||||
|
@ -133,5 +194,215 @@ module.exports = function(s,config,lang){
|
|||
cloudDiskUseStartupExtensions: cloudDiskUseStartupForAmazonS3,
|
||||
beforeAccountSave: beforeAccountSaveForAmazonS3,
|
||||
onAccountSave: cloudDiskUseStartupForAmazonS3,
|
||||
onInsertTimelapseFrame: onInsertTimelapseFrame,
|
||||
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud
|
||||
})
|
||||
}
|
||||
//return fields that will appear in settings
|
||||
return {
|
||||
"evaluation": "details.use_aws_s3 !== '0'",
|
||||
"name": lang["Amazon S3"],
|
||||
"color": "forestgreen",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=aws_s3_save",
|
||||
"selector":"autosave_aws_s3",
|
||||
"field": lang.Autosave,
|
||||
"description": "",
|
||||
"default": lang.No,
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Bucket,
|
||||
"name": "detail=aws_s3_bucket",
|
||||
"placeholder": "Example : slippery-seal",
|
||||
"form-group-class": "autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.aws_accessKeyId,
|
||||
"name": "detail=aws_accessKeyId",
|
||||
"form-group-class": "autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=aws_secretAccessKey",
|
||||
"fieldType":"password",
|
||||
"placeholder": "",
|
||||
"field": lang.aws_secretAccessKey,
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=aws_region",
|
||||
"field": lang.Region,
|
||||
"fieldType": "select",
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": "US West (N. California)",
|
||||
"value": "us-west-1"
|
||||
},
|
||||
{
|
||||
"name": "US West (Oregon)",
|
||||
"value": "us-west-2"
|
||||
},
|
||||
{
|
||||
"name": "US East (Ohio)",
|
||||
"value": "us-east-2"
|
||||
},
|
||||
{
|
||||
"name": "US East (N. Virginia)",
|
||||
"value": "us-east-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Mumbai)",
|
||||
"value": "ap-south-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Seoul)",
|
||||
"value": "ap-northeast-2"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Osaka-Local)**",
|
||||
"value": "ap-northeast-3"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Singapore)",
|
||||
"value": "ap-southeast-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Sydney)",
|
||||
"value": "ap-southeast-2"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific (Tokyo)",
|
||||
"value": "ap-northeast-1"
|
||||
},
|
||||
{
|
||||
"name": "Canada (Central)",
|
||||
"value": "ca-central-1"
|
||||
},
|
||||
{
|
||||
"name": "China (Beijing)",
|
||||
"value": "cn-north-1"
|
||||
},
|
||||
{
|
||||
"name": "China (Ningxia)",
|
||||
"value": "cn-northwest-1"
|
||||
},
|
||||
{
|
||||
"name": "EU (Frankfurt)",
|
||||
"value": "eu-central-1"
|
||||
},
|
||||
{
|
||||
"name": "EU (Ireland)",
|
||||
"value": "eu-west-1"
|
||||
},
|
||||
{
|
||||
"name": "EU (London)",
|
||||
"value": "eu-west-2"
|
||||
},
|
||||
{
|
||||
"name": "EU (Paris)",
|
||||
"value": "eu-west-3"
|
||||
},
|
||||
{
|
||||
"name": "South America (São Paulo)",
|
||||
"value": "sa-east-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=aws_s3_log",
|
||||
"field": lang['Save Links to Database'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_s3sld",
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=use_aws_s3_size_limit",
|
||||
"field": lang['Use Max Storage Amount'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_s3zl",
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"form-group-class-pre-layer":"h_s3sld_input h_s3sld_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=aws_s3_size_limit",
|
||||
"field": lang['Max Storage Amount'],
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"form-group-class-pre-layer":"h_s3sld_input h_s3sld_1",
|
||||
"description": "",
|
||||
"default": "10000",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=aws_s3_dir",
|
||||
"field": lang['Save Directory'],
|
||||
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
|
||||
"description": "",
|
||||
"default": "/",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
|
@ -167,4 +167,127 @@ module.exports = function(s,config,lang){
|
|||
beforeAccountSave: beforeAccountSaveForBackblazeB2,
|
||||
onAccountSave: cloudDiskUseStartupForBackblazeB2,
|
||||
})
|
||||
return {
|
||||
"evaluation": "details.use_bb_b2 !== '0'",
|
||||
"name": lang["Backblaze B2"],
|
||||
"color": "forestgreen",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=bb_b2_save",
|
||||
"selector":"autosave_bb_b2",
|
||||
"field": lang.Autosave,
|
||||
"description": "",
|
||||
"default": lang.No,
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Bucket,
|
||||
"name": "detail=bb_b2_bucket",
|
||||
"placeholder": "Example : slippery-seal",
|
||||
"form-group-class": "autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.keyId,
|
||||
"name": "detail=bb_b2_accountId",
|
||||
"form-group-class": "autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=bb_b2_applicationKey",
|
||||
"fieldType":"password",
|
||||
"placeholder": "XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXX",
|
||||
"field": lang.applicationKey,
|
||||
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=bb_b2_log",
|
||||
"field": lang['Save Links to Database'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_b2sld",
|
||||
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=use_bb_b2_size_limit",
|
||||
"field": lang['Use Max Storage Amount'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_b2zl",
|
||||
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"form-group-class-pre-layer":"h_b2sld_input h_b2sld_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=bb_b2_size_limit",
|
||||
"field": lang['Max Storage Amount'],
|
||||
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"form-group-class-pre-layer":"h_b2sld_input h_b2sld_1",
|
||||
"description": "",
|
||||
"default": "10000",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=bb_b2_dir",
|
||||
"field": lang['Save Directory'],
|
||||
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
|
||||
"description": "",
|
||||
"default": "/",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ module.exports = function(s){
|
|||
s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender)
|
||||
s.deleteVideoFromCloudExtensions[opt.name] = opt.deleteVideoFromCloudExtensions
|
||||
s.cloudDiskUseStartupExtensions[opt.name] = opt.cloudDiskUseStartupExtensions
|
||||
if(opt.onInsertTimelapseFrame)s.onInsertTimelapseFrame(opt.onInsertTimelapseFrame)
|
||||
if(opt.onDeleteTimelapseFrameFromCloud)s.onDeleteTimelapseFrameFromCloudExtensions[opt.name] = opt.onDeleteTimelapseFrameFromCloud
|
||||
s.beforeAccountSave(opt.beforeAccountSave)
|
||||
s.onAccountSave(opt.onAccountSave)
|
||||
s.cloudDisksLoader(opt.name)
|
||||
|
@ -13,6 +15,7 @@ module.exports = function(s){
|
|||
s.loadGroupAppExtender(opt.loadGroupAppExtender)
|
||||
s.unloadGroupAppExtender(opt.unloadGroupAppExtender)
|
||||
s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender)
|
||||
if(opt.onInsertTimelapseFrame)s.onInsertTimelapseFrame(opt.onInsertTimelapseFrame)
|
||||
s.beforeAccountSave(opt.beforeAccountSave)
|
||||
s.onAccountSave(opt.onAccountSave)
|
||||
s.onMonitorSave(opt.onMonitorSave)
|
||||
|
|
|
@ -34,8 +34,6 @@ module.exports = function(s,config,lang){
|
|||
userDetails.whcs_accessKeyId !== ''&&
|
||||
userDetails.whcs_secretAccessKey &&
|
||||
userDetails.whcs_secretAccessKey !== ''&&
|
||||
userDetails.whcs_region &&
|
||||
userDetails.whcs_region !== ''&&
|
||||
userDetails.whcs_bucket !== ''
|
||||
){
|
||||
if(!userDetails.whcs_dir || userDetails.whcs_dir === '/'){
|
||||
|
@ -50,6 +48,10 @@ module.exports = function(s,config,lang){
|
|||
if(!userDetails.whcs_endpoint || 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()
|
||||
|
@ -62,7 +64,7 @@ module.exports = function(s,config,lang){
|
|||
endpoint: wasabiEndpoint,
|
||||
accessKeyId: userDetails.whcs_accessKeyId,
|
||||
secretAccessKey: userDetails.whcs_secretAccessKey,
|
||||
region: userDetails.whcs_region
|
||||
region: whcs_region
|
||||
})
|
||||
s.group[e.ke].whcs = new s.group[e.ke].whcs.S3();
|
||||
}
|
||||
|
@ -100,9 +102,10 @@ module.exports = function(s,config,lang){
|
|||
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
|
||||
s.group[e.ke].whcs.upload({
|
||||
Bucket: s.group[e.ke].init.whcs_bucket,
|
||||
Bucket: bucketName,
|
||||
Key: saveLocation,
|
||||
Body:fileStream,
|
||||
ACL:'public-read',
|
||||
|
@ -112,6 +115,8 @@ module.exports = function(s,config,lang){
|
|||
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
|
||||
}
|
||||
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
|
||||
var cloudLink = data.Location
|
||||
cloudLink = fixCloudianUrl(e,cloudLink)
|
||||
var save = [
|
||||
e.mid,
|
||||
e.ke,
|
||||
|
@ -123,7 +128,7 @@ module.exports = function(s,config,lang){
|
|||
}),
|
||||
k.filesize,
|
||||
k.endTime,
|
||||
data.Location
|
||||
cloudLink
|
||||
]
|
||||
s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
|
@ -135,6 +140,83 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
}
|
||||
}
|
||||
var onInsertTimelapseFrame = function(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)
|
||||
fileStream.on('error', function (err) {
|
||||
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({
|
||||
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})
|
||||
}
|
||||
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
|
||||
var save = [
|
||||
queryInfo.mid,
|
||||
queryInfo.ke,
|
||||
queryInfo.time,
|
||||
s.s({
|
||||
type : 'whcs',
|
||||
location : saveLocation,
|
||||
}),
|
||||
queryInfo.size,
|
||||
data.Location
|
||||
]
|
||||
s.sqlQuery('INSERT INTO `Cloud Timelapse Frames` (mid,ke,time,details,size,href) VALUES (?,?,?,?,?,?)',save)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
amount : s.kilobyteToMegabyte(queryInfo.size),
|
||||
storageType : 'whcs'
|
||||
},'timelapseFrames')
|
||||
s.purgeCloudDiskForGroup(e,'whcs','timelapseFrames')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
|
||||
// e = user
|
||||
try{
|
||||
var frameDetails = JSON.parse(frame.details)
|
||||
}catch(err){
|
||||
var frameDetails = frame.details
|
||||
}
|
||||
if(frameDetails.type !== 'whcs'){
|
||||
return
|
||||
}
|
||||
if(!frameDetails.location){
|
||||
frameDetails.location = frame.href.split(locationUrl)[1]
|
||||
}
|
||||
s.group[e.ke].whcs.deleteObject({
|
||||
Bucket: s.group[e.ke].init.whcs_bucket,
|
||||
Key: frameDetails.location,
|
||||
}, function(err, data) {
|
||||
if (err) console.log(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
|
||||
}
|
||||
//wasabi
|
||||
s.addCloudUploader({
|
||||
name: 'whcs',
|
||||
|
@ -145,5 +227,250 @@ module.exports = function(s,config,lang){
|
|||
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWasabiHotCloudStorage,
|
||||
beforeAccountSave: beforeAccountSaveForWasabiHotCloudStorage,
|
||||
onAccountSave: cloudDiskUseStartupForWasabiHotCloudStorage,
|
||||
onInsertTimelapseFrame: onInsertTimelapseFrame,
|
||||
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud
|
||||
})
|
||||
return {
|
||||
"evaluation": "details.use_whcs !== '0'",
|
||||
"name": lang["S3-Based Network Storage"],
|
||||
"color": "forestgreen",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=whcs_save",
|
||||
"selector":"autosave_whcs",
|
||||
"field": lang.Autosave,
|
||||
"description": "",
|
||||
"default": lang.No,
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Bucket,
|
||||
"name": "detail=whcs_bucket",
|
||||
"placeholder": "Example : slippery-seal",
|
||||
"form-group-class": "autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.aws_accessKeyId,
|
||||
"name": "detail=whcs_accessKeyId",
|
||||
"form-group-class": "autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=whcs_secretAccessKey",
|
||||
"fieldType":"password",
|
||||
"placeholder": "",
|
||||
"field": lang.aws_secretAccessKey,
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=whcs_region",
|
||||
"field": lang.Region,
|
||||
"fieldType": "select",
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang['No Region'],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "US West 1",
|
||||
"value": "us-west-1"
|
||||
},
|
||||
{
|
||||
"name": "US West 2)",
|
||||
"value": "us-west-2"
|
||||
},
|
||||
{
|
||||
"name": "US East 1",
|
||||
"value": "us-east-2"
|
||||
},
|
||||
{
|
||||
"name": "US East 2",
|
||||
"value": "us-east-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 1",
|
||||
"value": "ap-south-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 2",
|
||||
"value": "ap-northeast-2"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 3",
|
||||
"value": "ap-northeast-3"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 4",
|
||||
"value": "ap-southeast-1"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 5",
|
||||
"value": "ap-southeast-2"
|
||||
},
|
||||
{
|
||||
"name": "Asia Pacific 6",
|
||||
"value": "ap-northeast-1"
|
||||
},
|
||||
{
|
||||
"name": "Canada 1",
|
||||
"value": "ca-central-1"
|
||||
},
|
||||
{
|
||||
"name": "China 1",
|
||||
"value": "cn-north-1"
|
||||
},
|
||||
{
|
||||
"name": "China 1",
|
||||
"value": "cn-northwest-1"
|
||||
},
|
||||
{
|
||||
"name": "EU 1",
|
||||
"value": "eu-central-1"
|
||||
},
|
||||
{
|
||||
"name": "EU 2",
|
||||
"value": "eu-west-1"
|
||||
},
|
||||
{
|
||||
"name": "EU 3",
|
||||
"value": "eu-west-2"
|
||||
},
|
||||
{
|
||||
"name": "EU 4",
|
||||
"value": "eu-west-3"
|
||||
},
|
||||
{
|
||||
"name": "South America 1",
|
||||
"value": "sa-east-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=whcs_log",
|
||||
"field": lang['Save Links to Database'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_whcssld",
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=use_whcs_size_limit",
|
||||
"field": lang['Use Max Storage Amount'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_whcszl",
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"form-group-class-pre-layer":"h_whcssld_input h_whcssld_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=whcs_size_limit",
|
||||
"field": lang['Max Storage Amount'],
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"form-group-class-pre-layer":"h_whcssld_input h_whcssld_1",
|
||||
"description": "",
|
||||
"default": "10000",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=whcs_dir",
|
||||
"field": lang['Save Directory'],
|
||||
"form-group-class":"autosave_whcs_input autosave_whcs_1",
|
||||
"description": "",
|
||||
"default": "/",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,8 +72,8 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
var onAccountSaveForSftp = function(group,userDetails,user){
|
||||
if(s.group[user.ke] && s.group[user.ke].sftp && s.group[user.ke].init.use_sftp !== '0' && s.group[user.ke].init.sftp_save === '1'){
|
||||
Object.keys(s.group[user.ke].mon_conf).forEach(function(monitorId){
|
||||
createSftpDirectory(s.group[user.ke].mon_conf[monitorId])
|
||||
Object.keys(s.group[user.ke].rawMonitorConfigurations).forEach(function(monitorId){
|
||||
createSftpDirectory(s.group[user.ke].rawMonitorConfigurations[monitorId])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -87,4 +87,92 @@ module.exports = function(s,config,lang){
|
|||
onAccountSave: onAccountSaveForSftp,
|
||||
onMonitorSave: onMonitorSaveForSftp,
|
||||
})
|
||||
return {
|
||||
"evaluation": "details.use_sftp !== '0'",
|
||||
"name": lang['SFTP (SSH File Transfer)'],
|
||||
"color": "forestgreen",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=sftp_save",
|
||||
"selector":"autosave_sftp",
|
||||
"field": lang.Autosave,
|
||||
"description": "",
|
||||
"default": lang.No,
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Host,
|
||||
"name": "detail=sftp_host",
|
||||
"form-group-class": "autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Port,
|
||||
"name": "detail=sftp_port",
|
||||
"form-group-class": "autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Username,
|
||||
"name": "detail=sftp_username",
|
||||
"form-group-class": "autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Password,
|
||||
"fieldType": "password",
|
||||
"name": "detail=sftp_password",
|
||||
"form-group-class": "autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.privateKey,
|
||||
"fieldType": "textarea",
|
||||
"name": "detail=sftp_privateKey",
|
||||
"form-group-class": "autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=sftp_dir",
|
||||
"field": lang['Save Directory'],
|
||||
"form-group-class":"autosave_sftp_input autosave_sftp_1",
|
||||
"description": "",
|
||||
"default": "/",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ module.exports = function(s,config,lang){
|
|||
if(wfs && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === "1"){
|
||||
var webdavUploadDir = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/'
|
||||
var startWebDavUpload = function(){
|
||||
s.group[e.ke].mon[e.id].webdavDirExist = true
|
||||
s.group[e.ke].activeMonitors[e.id].webdavDirExist = true
|
||||
var wfsWriteStream =
|
||||
fs.createReadStream(k.dir + k.filename).pipe(wfs.createWriteStream(webdavUploadDir + k.filename))
|
||||
if(s.group[e.ke].init.webdav_log === '1'){
|
||||
|
@ -102,7 +102,7 @@ module.exports = function(s,config,lang){
|
|||
s.purgeCloudDiskForGroup(e,'webdav')
|
||||
}
|
||||
}
|
||||
if(s.group[e.ke].mon[e.id].webdavDirExist !== true){
|
||||
if(s.group[e.ke].activeMonitors[e.id].webdavDirExist !== true){
|
||||
//check if webdav dir exist
|
||||
var parentPoint = 0
|
||||
var webDavParentz = webdavUploadDir.split('/')
|
||||
|
@ -166,4 +166,125 @@ module.exports = function(s,config,lang){
|
|||
beforeAccountSave: beforeAccountSaveForWebDav,
|
||||
onAccountSave: cloudDiskUseStartupForWebDav,
|
||||
})
|
||||
return {
|
||||
"evaluation": "details.use_webdav !== '0'",
|
||||
"name": lang.WebDAV,
|
||||
"color": "forestgreen",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=webdav_save",
|
||||
"selector":"autosave_webdav",
|
||||
"field": lang.Autosave,
|
||||
"description": "",
|
||||
"default": lang.No,
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.URL,
|
||||
"name": "detail=webdav_url",
|
||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Username,
|
||||
"name": "detail=webdav_user",
|
||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"field": lang.Password,
|
||||
"fieldType": "password",
|
||||
"name": "detail=webdav_pass",
|
||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=webdav_log",
|
||||
"field": lang['Save Links to Database'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_webdavsld",
|
||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=use_webdav_size_limit",
|
||||
"field": lang['Use Max Storage Amount'],
|
||||
"fieldType": "select",
|
||||
"selector": "h_webdavzl",
|
||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.No,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": lang.Yes,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=webdav_size_limit",
|
||||
"field": lang['Max Storage Amount'],
|
||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
||||
"description": "",
|
||||
"default": "10000",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "detail=webdav_dir",
|
||||
"field": lang['Save Directory'],
|
||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||
"description": "",
|
||||
"default": "/",
|
||||
"example": "",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
549
libs/user.js
549
libs/user.js
|
@ -2,80 +2,262 @@ var fs = require('fs');
|
|||
var events = require('events');
|
||||
var spawn = require('child_process').spawn;
|
||||
var exec = require('child_process').exec;
|
||||
module.exports = function(s,config){
|
||||
module.exports = function(s,config,lang){
|
||||
s.purgeDiskForGroup = function(e){
|
||||
if(config.cron.deleteOverMax === true){
|
||||
s.group[e.ke].sizePurgeQueue.push(1)
|
||||
if(s.group[e.ke].sizePurging !== true){
|
||||
s.group[e.ke].sizePurging = true
|
||||
var finish = function(){
|
||||
//remove value just used from queue
|
||||
s.group[e.ke].sizePurgeQueue.shift()
|
||||
//do next one
|
||||
if(s.group[e.ke].sizePurgeQueue.length > 0){
|
||||
checkQueue()
|
||||
if(config.cron.deleteOverMax === true && s.group[e.ke] && s.group[e.ke].sizePurgeQueue){
|
||||
s.group[e.ke].sizePurgeQueue.push(1)
|
||||
if(s.group[e.ke].sizePurging !== true){
|
||||
s.group[e.ke].sizePurging = true
|
||||
var finish = function(){
|
||||
//remove value just used from queue
|
||||
s.group[e.ke].sizePurgeQueue.shift()
|
||||
//do next one
|
||||
if(s.group[e.ke].sizePurgeQueue.length > 0){
|
||||
checkQueue()
|
||||
}else{
|
||||
s.group[e.ke].sizePurging = false
|
||||
s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
}
|
||||
var checkQueue = function(){
|
||||
//get first in queue
|
||||
var currentPurge = s.group[e.ke].sizePurgeQueue[0]
|
||||
var reRunCheck = function(){}
|
||||
var deleteSetOfVideos = function(err,videos,storageIndex,callback){
|
||||
var videosToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
var completedCheck = 0
|
||||
if(videos){
|
||||
videos.forEach(function(video){
|
||||
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
|
||||
videosToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(video.mid)
|
||||
queryValues.push(video.time)
|
||||
fs.chmod(video.dir,0o777,function(err){
|
||||
fs.unlink(video.dir,function(err){
|
||||
++completedCheck
|
||||
if(err){
|
||||
fs.stat(video.dir,function(err){
|
||||
if(!err){
|
||||
s.file('delete',video.dir)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(videosToDelete.length === completedCheck){
|
||||
videosToDelete = videosToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM Videos WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
|
||||
reRunCheck()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(e,{
|
||||
size: -(video.size/1000000),
|
||||
storageIndex: storageIndex
|
||||
})
|
||||
}else{
|
||||
s.setDiskUsedForGroup(e,-(video.size/1000000))
|
||||
}
|
||||
s.tx({
|
||||
f: 'video_delete',
|
||||
ff: 'over_max',
|
||||
filename: s.formattedTime(video.time)+'.'+video.ext,
|
||||
mid: video.mid,
|
||||
ke: video.ke,
|
||||
time: video.time,
|
||||
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
||||
},'GRP_'+e.ke)
|
||||
})
|
||||
}else{
|
||||
s.group[e.ke].sizePurging = false
|
||||
s.sendDiskUsedAmountToClients(e)
|
||||
console.log(err)
|
||||
}
|
||||
if(videosToDelete.length === 0){
|
||||
if(callback)callback()
|
||||
}
|
||||
}
|
||||
var checkQueue = function(){
|
||||
//get first in queue
|
||||
var currentPurge = s.group[e.ke].sizePurgeQueue[0]
|
||||
var deleteVideos = function(){
|
||||
//run purge command
|
||||
if(s.group[e.ke].usedSpace > (s.group[e.ke].sizeLimit*config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,videos){
|
||||
var videosToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
var completedCheck = 0
|
||||
if(videos){
|
||||
videos.forEach(function(video){
|
||||
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
|
||||
videosToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(video.mid)
|
||||
queryValues.push(video.time)
|
||||
fs.chmod(video.dir,0o777,function(err){
|
||||
fs.unlink(video.dir,function(err){
|
||||
++completedCheck
|
||||
if(err){
|
||||
fs.stat(video.dir,function(err){
|
||||
if(!err){
|
||||
s.file('delete',video.dir)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(videosToDelete.length === completedCheck){
|
||||
videosToDelete = videosToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM Videos WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
|
||||
deleteVideos()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
s.setDiskUsedForGroup(e,-(video.size/1000000))
|
||||
s.tx({
|
||||
f: 'video_delete',
|
||||
ff: 'over_max',
|
||||
filename: s.formattedTime(video.time)+'.'+video.ext,
|
||||
mid: video.mid,
|
||||
ke: video.ke,
|
||||
time: video.time,
|
||||
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
||||
},'GRP_'+e.ke)
|
||||
var deleteSetOfTimelapseFrames = function(err,frames,storageIndex,callback){
|
||||
var framesToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
var completedCheck = 0
|
||||
if(frames){
|
||||
frames.forEach(function(frame){
|
||||
var selectedDate = frame.filename.split('T')[0]
|
||||
var dir = s.getTimelapseFrameDirectory(frame)
|
||||
var fileLocationMid = `${dir}` + frame.filename
|
||||
framesToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(frame.mid)
|
||||
queryValues.push(frame.time)
|
||||
fs.unlink(fileLocationMid,function(err){
|
||||
++completedCheck
|
||||
if(err){
|
||||
fs.stat(fileLocationMid,function(err){
|
||||
if(!err){
|
||||
s.file('delete',fileLocationMid)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
console.log(err)
|
||||
}
|
||||
if(videosToDelete.length === 0){
|
||||
finish()
|
||||
if(framesToDelete.length === completedCheck){
|
||||
framesToDelete = framesToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM `Timelapse Frames` WHERE ke =? AND ('+framesToDelete+')',queryValues,function(){
|
||||
reRunCheck()
|
||||
})
|
||||
}
|
||||
})
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(e,{
|
||||
size: -(frame.size/1000000),
|
||||
storageIndex: storageIndex
|
||||
},'timelapeFrames')
|
||||
}else{
|
||||
s.setDiskUsedForGroup(e,-(frame.size/1000000),'timelapeFrames')
|
||||
}
|
||||
// s.tx({
|
||||
// f: 'timelapse_frame_delete',
|
||||
// ff: 'over_max',
|
||||
// filename: s.formattedTime(video.time)+'.'+video.ext,
|
||||
// mid: video.mid,
|
||||
// ke: video.ke,
|
||||
// time: video.time,
|
||||
// end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
||||
// },'GRP_'+e.ke)
|
||||
})
|
||||
}else{
|
||||
finish()
|
||||
console.log(err)
|
||||
}
|
||||
if(framesToDelete.length === 0){
|
||||
if(callback)callback()
|
||||
}
|
||||
}
|
||||
deleteVideos()
|
||||
var deleteSetOfFileBinFiles = function(err,files,storageIndex,callback){
|
||||
var filesToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
var completedCheck = 0
|
||||
if(files){
|
||||
files.forEach(function(file){
|
||||
var dir = s.getFileBinDirectory(file)
|
||||
var fileLocationMid = `${dir}` + file.name
|
||||
filesToDelete.push('(mid=? AND `name`=?)')
|
||||
queryValues.push(file.mid)
|
||||
queryValues.push(file.name)
|
||||
fs.unlink(fileLocationMid,function(err){
|
||||
++completedCheck
|
||||
if(err){
|
||||
fs.stat(fileLocationMid,function(err){
|
||||
if(!err){
|
||||
s.file('delete',fileLocationMid)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(filesToDelete.length === completedCheck){
|
||||
filesToDelete = filesToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM `Files` WHERE ke =? AND ('+filesToDelete+')',queryValues,function(){
|
||||
reRunCheck()
|
||||
})
|
||||
}
|
||||
})
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(e,{
|
||||
size: -(file.size/1000000),
|
||||
storageIndex: storageIndex
|
||||
},'fileBin')
|
||||
}else{
|
||||
s.setDiskUsedForGroup(e,-(file.size/1000000),'fileBin')
|
||||
}
|
||||
})
|
||||
}else{
|
||||
console.log(err)
|
||||
}
|
||||
if(framesToDelete.length === 0){
|
||||
if(callback)callback()
|
||||
}
|
||||
}
|
||||
var deleteMainVideos = function(callback){
|
||||
reRunCheck = function(){
|
||||
return deleteMainVideos(callback)
|
||||
}
|
||||
//run purge command
|
||||
if(s.group[e.ke].usedSpaceVideos > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? AND details NOT LIKE \'%"dir"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,rows){
|
||||
deleteSetOfVideos(err,rows,null,callback)
|
||||
})
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
var deleteAddStorageVideos = function(callback){
|
||||
reRunCheck = function(){
|
||||
return deleteAddStorageVideos(callback)
|
||||
}
|
||||
var currentStorageNumber = 0
|
||||
var readStorageArray = function(finishedReading){
|
||||
setTimeout(function(){
|
||||
reRunCheck = readStorageArray
|
||||
var storage = s.listOfStorage[currentStorageNumber]
|
||||
if(!storage){
|
||||
//done all checks, move on to next user
|
||||
callback()
|
||||
return
|
||||
}
|
||||
var storageId = storage.value
|
||||
if(storageId === '' || !s.group[e.ke].addStorageUse[storageId]){
|
||||
++currentStorageNumber
|
||||
readStorageArray()
|
||||
return
|
||||
}
|
||||
var storageIndex = s.group[e.ke].addStorageUse[storageId]
|
||||
//run purge command
|
||||
if(storageIndex.usedSpace > (storageIndex.sizeLimit * (storageIndex.deleteOffset || config.cron.deleteOverMaxOffset))){
|
||||
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? AND details LIKE ? ORDER BY `time` ASC LIMIT 3',[e.ke,`%"dir":"${storage.value}"%`],function(err,rows){
|
||||
deleteSetOfVideos(err,rows,storageIndex,callback)
|
||||
})
|
||||
}else{
|
||||
++currentStorageNumber
|
||||
readStorageArray()
|
||||
}
|
||||
})
|
||||
}
|
||||
readStorageArray()
|
||||
}
|
||||
var deleteTimelapseFrames = function(callback){
|
||||
reRunCheck = function(){
|
||||
return deleteTimelapseFrames(callback)
|
||||
}
|
||||
//run purge command
|
||||
if(s.group[e.ke].usedSpaceTimelapseFrames > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitTimelapseFramesPercent / 100) * config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE ke=? AND details NOT LIKE \'%"archived":"1"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,frames){
|
||||
deleteSetOfTimelapseFrames(err,frames,null,callback)
|
||||
})
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
var deleteFileBinFiles = function(callback){
|
||||
if(config.deleteFileBinsOverMax === true){
|
||||
reRunCheck = function(){
|
||||
return deleteSetOfFileBinFiles(callback)
|
||||
}
|
||||
//run purge command
|
||||
if(s.group[e.ke].usedSpaceFileBin > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitFileBinPercent / 100) * config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM `Files` WHERE ke=? ORDER BY `time` ASC LIMIT 1',[e.ke],function(err,frames){
|
||||
deleteSetOfFileBinFiles(err,frames,null,callback)
|
||||
})
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
deleteMainVideos(function(){
|
||||
deleteTimelapseFrames(function(){
|
||||
deleteFileBinFiles(function(){
|
||||
deleteAddStorageVideos(function(){
|
||||
finish()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
checkQueue()
|
||||
}
|
||||
|
@ -83,27 +265,41 @@ module.exports = function(s,config){
|
|||
s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
}
|
||||
s.setDiskUsedForGroup = function(e,bytes){
|
||||
s.setDiskUsedForGroup = function(e,bytes,storagePoint){
|
||||
//`bytes` will be used as the value to add or substract
|
||||
if(s.group[e.ke] && s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter.emit('set',bytes)
|
||||
s.group[e.ke].diskUsedEmitter.emit('set',bytes,storagePoint)
|
||||
}
|
||||
}
|
||||
s.purgeCloudDiskForGroup = function(e,storageType){
|
||||
if(s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter.emit('purgeCloud',storageType)
|
||||
s.setDiskUsedForGroupAddStorage = function(e,data,storagePoint){
|
||||
if(s.group[e.ke] && s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter.emit('setAddStorage',data,storagePoint)
|
||||
}
|
||||
}
|
||||
s.setCloudDiskUsedForGroup = function(e,usage){
|
||||
//`bytes` will be used as the value to add or substract
|
||||
s.purgeCloudDiskForGroup = function(e,storageType,storagePoint){
|
||||
if(s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter.emit('setCloud',usage)
|
||||
s.group[e.ke].diskUsedEmitter.emit('purgeCloud',storageType,storagePoint)
|
||||
}
|
||||
}
|
||||
s.setCloudDiskUsedForGroup = function(e,usage,storagePoint){
|
||||
//`usage` will be used as the value to add or substract
|
||||
if(s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter.emit('setCloud',usage,storagePoint)
|
||||
}
|
||||
}
|
||||
s.sendDiskUsedAmountToClients = function(e){
|
||||
//send the amount used disk space to connected users
|
||||
if(s.group[e.ke]&&s.group[e.ke].init){
|
||||
s.tx({f:'diskUsed',size:s.group[e.ke].usedSpace,limit:s.group[e.ke].sizeLimit},'GRP_'+e.ke);
|
||||
s.tx({
|
||||
f: 'diskUsed',
|
||||
size: s.group[e.ke].usedSpace,
|
||||
usedSpace: s.group[e.ke].usedSpace,
|
||||
usedSpaceVideos: s.group[e.ke].usedSpaceVideos,
|
||||
usedSpaceFilebin: s.group[e.ke].usedSpaceFilebin,
|
||||
usedSpaceTimelapseFrames: s.group[e.ke].usedSpaceTimelapseFrames,
|
||||
limit: s.group[e.ke].sizeLimit,
|
||||
addStorage: s.group[e.ke].addStorageUse
|
||||
},'GRP_'+e.ke);
|
||||
}
|
||||
}
|
||||
//user log
|
||||
|
@ -125,15 +321,20 @@ module.exports = function(s,config){
|
|||
if(!s.group[e.ke].init){
|
||||
s.group[e.ke].init={}
|
||||
}
|
||||
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse={}};
|
||||
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
|
||||
if(!s.group[e.ke].users){s.group[e.ke].users={}}
|
||||
if(!s.group[e.ke].dashcamUsers){s.group[e.ke].dashcamUsers={}}
|
||||
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
|
||||
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse = {}}
|
||||
if(!e.limit||e.limit===''){e.limit=10000}else{e.limit=parseFloat(e.limit)}
|
||||
//save global space limit for group key (mb)
|
||||
s.group[e.ke].sizeLimit=e.limit;
|
||||
s.group[e.ke].sizeLimit = e.limit || s.group[e.ke].sizeLimit || 10000
|
||||
s.group[e.ke].sizeLimitVideoPercent = parseFloat(s.group[e.ke].init.size_video_percent) || 90
|
||||
s.group[e.ke].sizeLimitTimelapseFramesPercent = parseFloat(s.group[e.ke].init.size_timelapse_percent) || 5
|
||||
s.group[e.ke].sizeLimitFileBinPercent = parseFloat(s.group[e.ke].init.size_filebin_percent) || 5
|
||||
//save global used space as megabyte value
|
||||
s.group[e.ke].usedSpace=e.size/1000000;
|
||||
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace || ((e.size || 0) / 1000000)
|
||||
//emit the changes to connected users
|
||||
s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
|
@ -143,9 +344,9 @@ module.exports = function(s,config){
|
|||
s.group[e.ke].init={};
|
||||
}
|
||||
s.sqlQuery('SELECT * FROM Users WHERE ke=? AND details NOT LIKE ?',[e.ke,'%"sub"%'],function(ar,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0];
|
||||
ar=JSON.parse(r.details);
|
||||
if(r && r[0]){
|
||||
r = r[0];
|
||||
ar = JSON.parse(r.details);
|
||||
//load extenders
|
||||
s.loadGroupAppExtensions.forEach(function(extender){
|
||||
extender(r,ar)
|
||||
|
@ -153,7 +354,7 @@ module.exports = function(s,config){
|
|||
//disk Used Emitter
|
||||
if(!s.group[e.ke].diskUsedEmitter){
|
||||
s.group[e.ke].diskUsedEmitter = new events.EventEmitter()
|
||||
s.group[e.ke].diskUsedEmitter.on('setCloud',function(currentChange){
|
||||
s.group[e.ke].diskUsedEmitter.on('setCloud',function(currentChange,storagePoint){
|
||||
var amount = currentChange.amount
|
||||
var storageType = currentChange.storageType
|
||||
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
|
||||
|
@ -168,52 +369,96 @@ module.exports = function(s,config){
|
|||
}
|
||||
//change global size value
|
||||
cloudDisk.usedSpace = cloudDisk.usedSpace + amount
|
||||
switch(storagePoint){
|
||||
case'timelapeFrames':
|
||||
cloudDisk.usedSpaceTimelapseFrames += amount
|
||||
break;
|
||||
case'fileBin':
|
||||
cloudDisk.usedSpaceFilebin += amount
|
||||
break;
|
||||
default:
|
||||
cloudDisk.usedSpaceVideos += amount
|
||||
break;
|
||||
}
|
||||
})
|
||||
s.group[e.ke].diskUsedEmitter.on('purgeCloud',function(storageType){
|
||||
s.group[e.ke].diskUsedEmitter.on('purgeCloud',function(storageType,storagePoint){
|
||||
if(config.cron.deleteOverMax === true){
|
||||
//set queue processor
|
||||
var finish=function(){
|
||||
// s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
var deleteVideos = function(){
|
||||
//run purge command
|
||||
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
|
||||
if(cloudDisk.sizeLimitCheck && cloudDisk.usedSpace > (cloudDisk.sizeLimit*config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE status != 0 AND ke=? AND details LIKE \'%"type":"'+storageType+'"%\' ORDER BY `time` ASC LIMIT 2',[e.ke],function(err,videos){
|
||||
var videosToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
if(!videos)return console.log(err)
|
||||
videos.forEach(function(video){
|
||||
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
|
||||
videosToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(video.mid)
|
||||
queryValues.push(video.time)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
amount : -(video.size/1000000),
|
||||
storageType : storageType
|
||||
})
|
||||
s.deleteVideoFromCloudExtensionsRunner(e,storageType,video)
|
||||
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
|
||||
//set queue processor
|
||||
var finish=function(){
|
||||
// s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
var deleteVideos = function(){
|
||||
//run purge command
|
||||
if(cloudDisk.sizeLimitCheck && cloudDisk.usedSpace > (cloudDisk.sizeLimit*config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE status != 0 AND ke=? AND details LIKE \'%"type":"'+storageType+'"%\' ORDER BY `time` ASC LIMIT 2',[e.ke],function(err,videos){
|
||||
var videosToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
if(!videos)return console.log(err)
|
||||
videos.forEach(function(video){
|
||||
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
|
||||
videosToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(video.mid)
|
||||
queryValues.push(video.time)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
amount : -(video.size/1000000),
|
||||
storageType : storageType
|
||||
})
|
||||
if(videosToDelete.length > 0){
|
||||
videosToDelete = videosToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM `Cloud Videos` WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
|
||||
deleteVideos()
|
||||
})
|
||||
}else{
|
||||
finish()
|
||||
}
|
||||
s.deleteVideoFromCloudExtensionsRunner(e,storageType,video)
|
||||
})
|
||||
}else{
|
||||
finish()
|
||||
}
|
||||
if(videosToDelete.length > 0){
|
||||
videosToDelete = videosToDelete.join(' OR ')
|
||||
s.sqlQuery('DELETE FROM `Cloud Videos` WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
|
||||
deleteVideos()
|
||||
})
|
||||
}else{
|
||||
finish()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
finish()
|
||||
}
|
||||
deleteVideos()
|
||||
}
|
||||
var deleteTimelapseFrames = function(callback){
|
||||
reRunCheck = function(){
|
||||
return deleteTimelapseFrames(callback)
|
||||
}
|
||||
//run purge command
|
||||
if(cloudDisk.usedSpaceTimelapseFrames > (cloudDisk.sizeLimit * (s.group[e.ke].sizeLimitTimelapseFramesPercent / 100) * config.cron.deleteOverMaxOffset)){
|
||||
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE ke=? AND details NOT LIKE \'%"archived":"1"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,frames){
|
||||
var framesToDelete = []
|
||||
var queryValues = [e.ke]
|
||||
if(!frames)return console.log(err)
|
||||
frames.forEach(function(frame){
|
||||
frame.dir = s.getVideoDirectory(frame) + s.formattedTime(frame.time) + '.' + frame.ext
|
||||
framesToDelete.push('(mid=? AND `time`=?)')
|
||||
queryValues.push(frame.mid)
|
||||
queryValues.push(frame.time)
|
||||
s.setCloudDiskUsedForGroup(e,{
|
||||
amount : -(frame.size/1000000),
|
||||
storageType : storageType
|
||||
})
|
||||
s.deleteVideoFromCloudExtensionsRunner(e,storageType,frame)
|
||||
})
|
||||
s.sqlQuery('DELETE FROM `Cloud Timelapse Frames` WHERE ke =? AND ('+framesToDelete+')',queryValues,function(){
|
||||
deleteTimelapseFrames(callback)
|
||||
})
|
||||
})
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}
|
||||
deleteVideos(function(){
|
||||
deleteTimelapseFrames(function(){
|
||||
|
||||
})
|
||||
})
|
||||
}else{
|
||||
// s.sendDiskUsedAmountToClients(e)
|
||||
}
|
||||
})
|
||||
//s.setDiskUsedForGroup
|
||||
s.group[e.ke].diskUsedEmitter.on('set',function(currentChange){
|
||||
s.group[e.ke].diskUsedEmitter.on('set',function(currentChange,storageType){
|
||||
//validate current values
|
||||
if(!s.group[e.ke].usedSpace){
|
||||
s.group[e.ke].usedSpace=0
|
||||
|
@ -225,12 +470,51 @@ module.exports = function(s,config){
|
|||
}
|
||||
//change global size value
|
||||
s.group[e.ke].usedSpace += currentChange
|
||||
switch(storageType){
|
||||
case'timelapeFrames':
|
||||
s.group[e.ke].usedSpaceTimelapseFrames += currentChange
|
||||
break;
|
||||
case'fileBin':
|
||||
s.group[e.ke].usedSpaceFilebin += currentChange
|
||||
break;
|
||||
default:
|
||||
s.group[e.ke].usedSpaceVideos += currentChange
|
||||
break;
|
||||
}
|
||||
//remove value just used from queue
|
||||
s.sendDiskUsedAmountToClients(e)
|
||||
})
|
||||
s.group[e.ke].diskUsedEmitter.on('setAddStorage',function(data,storageType){
|
||||
var currentSize = data.size
|
||||
var storageIndex = data.storageIndex
|
||||
//validate current values
|
||||
if(!storageIndex.usedSpace){
|
||||
storageIndex.usedSpace = 0
|
||||
}else{
|
||||
storageIndex.usedSpace = parseFloat(storageIndex.usedSpace)
|
||||
}
|
||||
if(storageIndex.usedSpace < 0 || isNaN(storageIndex.usedSpace)){
|
||||
storageIndex.usedSpace = 0
|
||||
}
|
||||
//change global size value
|
||||
storageIndex.usedSpace += currentSize
|
||||
switch(storageType){
|
||||
case'timelapeFrames':
|
||||
storageIndex.usedSpaceTimelapseFrames += currentSize
|
||||
break;
|
||||
case'fileBin':
|
||||
storageIndex.usedSpaceFilebin += currentSize
|
||||
break;
|
||||
default:
|
||||
storageIndex.usedSpaceVideos += currentSize
|
||||
break;
|
||||
}
|
||||
//remove value just used from queue
|
||||
s.sendDiskUsedAmountToClients(e)
|
||||
})
|
||||
}
|
||||
Object.keys(ar).forEach(function(v){
|
||||
s.group[e.ke].init[v]=ar[v]
|
||||
s.group[e.ke].init[v] = ar[v]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -259,6 +543,7 @@ module.exports = function(s,config){
|
|||
d.form.details.edit_days=d.d.edit_days
|
||||
d.form.details.use_admin=d.d.use_admin
|
||||
d.form.details.use_ldap=d.d.use_ldap
|
||||
d.form.details.landing_page=d.d.landing_page
|
||||
//check
|
||||
if(d.d.edit_days == "0"){
|
||||
d.form.details.days = d.d.days;
|
||||
|
@ -278,8 +563,38 @@ module.exports = function(s,config){
|
|||
if(d.d.days){d.form.details.days=d.d.days;}
|
||||
delete(d.form.details.mon_groups)
|
||||
}
|
||||
var newSize = d.form.details.size || 10000
|
||||
d.form.details=JSON.stringify(d.form.details)
|
||||
var newSize = parseFloat(d.form.details.size) || 10000
|
||||
//load addStorageUse
|
||||
var currentStorageNumber = 0
|
||||
var readStorageArray = function(){
|
||||
var storage = s.listOfStorage[currentStorageNumber]
|
||||
if(!storage){
|
||||
//done all checks, move on to next user
|
||||
return
|
||||
}
|
||||
var path = storage.value
|
||||
if(path === ''){
|
||||
++currentStorageNumber
|
||||
readStorageArray()
|
||||
return
|
||||
}
|
||||
var detailContainer = d.form.details || s.group[r.ke].init
|
||||
var storageId = path
|
||||
var detailsContainerAddStorage = s.parseJSON(detailContainer.addStorage)
|
||||
if(!s.group[d.ke].addStorageUse[storageId])s.group[d.ke].addStorageUse[storageId] = {}
|
||||
var storageIndex = s.group[d.ke].addStorageUse[storageId]
|
||||
storageIndex.name = storage.name
|
||||
storageIndex.path = path
|
||||
storageIndex.usedSpace = storageIndex.usedSpace || 0
|
||||
if(detailsContainerAddStorage && detailsContainerAddStorage[path] && detailsContainerAddStorage[path].limit){
|
||||
storageIndex.sizeLimit = parseFloat(detailsContainerAddStorage[path].limit)
|
||||
}else{
|
||||
storageIndex.sizeLimit = newSize
|
||||
}
|
||||
}
|
||||
readStorageArray()
|
||||
///
|
||||
d.form.details = JSON.stringify(d.form.details)
|
||||
///
|
||||
d.set=[],d.ar=[];
|
||||
if(d.form.pass&&d.form.pass!==''){d.form.pass=s.createHash(d.form.pass);}else{delete(d.form.pass)};
|
||||
|
|
20
libs/version.js
Normal file
20
libs/version.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
var exec = require('child_process').exec
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
var getRepositoryCommitId = function(callback){
|
||||
exec(`git rev-parse HEAD`,function(err,response){
|
||||
if(response){
|
||||
var data = response.toString()
|
||||
var isGitRespository = false
|
||||
if(data.indexOf('not a git repository') === -1){
|
||||
s.currentVersion = data
|
||||
isGitRespository = true
|
||||
s.systemLog(`Current Version : ${s.currentVersion}`)
|
||||
}
|
||||
}else if(err){
|
||||
s.debugLog('Git is not installed.')
|
||||
}
|
||||
if(callback)callback(!isGitRespository,data)
|
||||
})
|
||||
}
|
||||
getRepositoryCommitId()
|
||||
}
|
65
libs/videoDropInServer.js
Normal file
65
libs/videoDropInServer.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
var fs = require('fs')
|
||||
var express = require('express')
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
if(config.videoDropInServer === true){
|
||||
if(!config.videoDropInServerPort)config.videoDropInServerPort = 420
|
||||
if(!config.videoDropInServerUrl)config.videoDropInServerUrl = `ftp://0.0.0.0:${config.videoDropInServerPort}`
|
||||
config.videoDropInServerUrl = config.videoDropInServerUrl.replace('{{PORT}}',config.videoDropInServerPort)
|
||||
const FtpSrv = require('ftp-srv')
|
||||
const videoDropInServer = new FtpSrv({
|
||||
url: config.videoDropInServerUrl,
|
||||
// log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){}
|
||||
})
|
||||
|
||||
videoDropInServer.on('login', (data, resolve, reject) => {
|
||||
var username = data.username
|
||||
var password = data.password
|
||||
s.basicOrApiAuthentication(username,password,function(err,user){
|
||||
if(user){
|
||||
data.connection.on('STOR', (error, filePath) => {
|
||||
if(!error && filePath){
|
||||
var filenameParts = filePath.replace(s.dir.videos + user.ke + '/','').split('/')
|
||||
var ke = user.ke
|
||||
var mid = filenameParts[0].replace('_timelapse','')
|
||||
var monitor = s.group[ke].rawMonitorConfigurations[mid]
|
||||
var filename = filenameParts[filenameParts.length - 1]
|
||||
if(s.isCorrectFilenameSyntax(filename)){
|
||||
if(filenameParts[0].indexOf('_timelapse')){
|
||||
var fileStats = fs.statSync(filePath)
|
||||
var details = {}
|
||||
if(monitor.details && monitor.details.dir && monitor.details.dir !== ''){
|
||||
details.dir = monitor.details.dir
|
||||
}
|
||||
var timeNow = new Date(s.nameToTime(filename))
|
||||
s.sqlQuery('INSERT INTO `Timelapse Frames` (ke,mid,details,filename,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),filename,fileStats.size,timeNow])
|
||||
s.setDiskUsedForGroup(monitor,fileStats.size / 1000000)
|
||||
}
|
||||
// else{
|
||||
// s.insertDatabaseRow(
|
||||
// monitor,
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// )
|
||||
// console.log(filename)
|
||||
// }
|
||||
}else{
|
||||
console.log('Incorrect Filename Syntax')
|
||||
}
|
||||
}else{
|
||||
s.systemLog(error)
|
||||
}
|
||||
})
|
||||
resolve({root: s.dir.videos + user.ke})
|
||||
}else{
|
||||
// reject(new Error('Failed Authorization'))
|
||||
}
|
||||
})
|
||||
})
|
||||
videoDropInServer.listen().then(() => {
|
||||
s.systemLog(`Video Drop-In Server (FTP) running on port ${config.videoDropInServerPort}...`)
|
||||
}).catch(function(err){
|
||||
s.systemLog(err)
|
||||
})
|
||||
}
|
||||
}
|
178
libs/videos.js
178
libs/videos.js
|
@ -26,7 +26,7 @@ module.exports = function(s,config,lang){
|
|||
*/
|
||||
s.buildVideoLinks = function(videos,options){
|
||||
videos.forEach(function(v){
|
||||
var details = JSON.parse(v.details)
|
||||
var details = s.parseJSON(v.details)
|
||||
var queryString = []
|
||||
if(details.isUTC === true){
|
||||
queryString.push('isUTC=true')
|
||||
|
@ -45,7 +45,7 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
v.filename = s.formattedTime(v.time)+'.'+v.ext;
|
||||
if(!options.videoParam)options.videoParam = 'videos'
|
||||
var href = '/'+options.auth+'/'+options.videoParam+'/'+v.ke+'/'+v.mid+'/'+v.filename;
|
||||
var href = s.checkCorrectPathEnding(config.webPaths.apiPrefix) + options.auth+'/'+options.videoParam+'/'+v.ke+'/'+v.mid+'/'+v.filename;
|
||||
v.actionUrl = href
|
||||
v.links = {
|
||||
deleteVideo : href+'/delete' + queryString,
|
||||
|
@ -56,16 +56,11 @@ module.exports = function(s,config,lang){
|
|||
v.details = details
|
||||
})
|
||||
}
|
||||
//extender for "s.insertCompletedVideo"
|
||||
s.insertCompletedVideoExtensions = []
|
||||
s.insertCompletedVideoExtender = function(callback){
|
||||
s.insertCompletedVideoExtensions.push(callback)
|
||||
}
|
||||
s.insertDatabaseRow = function(e,k,callback){
|
||||
s.checkDetails(e)
|
||||
//save database row
|
||||
k.details = {}
|
||||
if(e.details&&e.details.dir&&e.details.dir!==''){
|
||||
if(!k.details)k.details = {}
|
||||
if(e.details && e.details.dir && e.details.dir !== ''){
|
||||
k.details.dir = e.details.dir
|
||||
}
|
||||
if(config.useUTC === true)k.details.isUTC = config.useUTC;
|
||||
|
@ -94,8 +89,8 @@ module.exports = function(s,config,lang){
|
|||
if(!k)k={};
|
||||
e.dir = s.getVideoDirectory(e)
|
||||
k.dir = e.dir.toString()
|
||||
if(s.group[e.ke].mon[e.id].childNode){
|
||||
s.cx({f:'insertCompleted',d:s.group[e.ke].mon_conf[e.id],k:k},s.group[e.ke].mon[e.id].childNodeId);
|
||||
if(s.group[e.ke].activeMonitors[e.id].childNode){
|
||||
s.cx({f:'insertCompleted',d:s.group[e.ke].rawMonitorConfigurations[e.id],k:k},s.group[e.ke].activeMonitors[e.id].childNodeId);
|
||||
}else{
|
||||
//get file directory
|
||||
k.fileExists = fs.existsSync(k.dir+k.file)
|
||||
|
@ -113,6 +108,7 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
if(k.fileExists===true){
|
||||
//close video row
|
||||
k.details = {}
|
||||
k.stat = fs.statSync(k.dir+k.file)
|
||||
k.filesize = k.stat.size
|
||||
k.filesizeMB = parseFloat((k.filesize/1000000).toFixed(2))
|
||||
|
@ -145,8 +141,8 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
})
|
||||
.on('close',function(){
|
||||
clearTimeout(s.group[e.ke].mon[e.id].recordingChecker)
|
||||
clearTimeout(s.group[e.ke].mon[e.id].streamChecker)
|
||||
clearTimeout(s.group[e.ke].activeMonitors[e.id].recordingChecker)
|
||||
clearTimeout(s.group[e.ke].activeMonitors[e.id].streamChecker)
|
||||
s.cx({
|
||||
f:'created_file',
|
||||
mid:e.id,
|
||||
|
@ -174,7 +170,18 @@ module.exports = function(s,config,lang){
|
|||
//purge over max
|
||||
s.purgeDiskForGroup(e)
|
||||
//send new diskUsage values
|
||||
s.setDiskUsedForGroup(e,k.filesizeMB)
|
||||
var storageIndex = s.getVideoStorageIndex(e)
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(e,{
|
||||
size: k.filesizeMB,
|
||||
storageIndex: storageIndex
|
||||
})
|
||||
}else{
|
||||
s.setDiskUsedForGroup(e,k.filesizeMB)
|
||||
}
|
||||
s.onBeforeInsertCompletedVideoExtensions.forEach(function(extender){
|
||||
extender(e,k)
|
||||
})
|
||||
s.insertDatabaseRow(e,k,callback)
|
||||
s.insertCompletedVideoExtensions.forEach(function(extender){
|
||||
extender(e,k)
|
||||
|
@ -216,7 +223,15 @@ module.exports = function(s,config,lang){
|
|||
time: s.nameToTime(filename),
|
||||
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
||||
},'GRP_'+e.ke);
|
||||
s.setDiskUsedForGroup(e,-(r.size / 1000000))
|
||||
var storageIndex = s.getVideoStorageIndex(e)
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(e,{
|
||||
size: -(r.size / 1000000),
|
||||
storageIndex: storageIndex
|
||||
})
|
||||
}else{
|
||||
s.setDiskUsedForGroup(e,-(r.size / 1000000))
|
||||
}
|
||||
s.sqlQuery('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',queryValues,function(err){
|
||||
if(err){
|
||||
s.systemLog(lang['File Delete Error'] + ' : '+e.ke+' : '+' : '+e.id,err)
|
||||
|
@ -270,7 +285,15 @@ module.exports = function(s,config,lang){
|
|||
time: s.nameToTime(filename),
|
||||
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
||||
},'GRP_'+video.ke);
|
||||
s.setDiskUsedForGroup(video,-(video.size / 1000000))
|
||||
var storageIndex = s.getVideoStorageIndex(video)
|
||||
if(storageIndex){
|
||||
s.setDiskUsedForGroupAddStorage(video,{
|
||||
size: -(video.size / 1000000),
|
||||
storageIndex: storageIndex
|
||||
})
|
||||
}else{
|
||||
s.setDiskUsedForGroup(video,-(video.size / 1000000))
|
||||
}
|
||||
fs.unlink(video.dir+filename,function(err){
|
||||
fs.stat(video.dir+filename,function(err){
|
||||
if(!err){
|
||||
|
@ -334,8 +357,9 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
if(forceCheck === true || config.insertOrphans === true){
|
||||
if(!checkMax){
|
||||
checkMax = config.orphanedVideoCheckMax
|
||||
checkMax = config.orphanedVideoCheckMax || 2
|
||||
}
|
||||
|
||||
var videosDirectory = s.getVideoDirectory(monitor)// + s.formattedTime(video.time) + '.' + video.ext
|
||||
fs.readdir(videosDirectory,function(err,files){
|
||||
if(files && files.length > 0){
|
||||
|
@ -380,7 +404,7 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
s.streamMp4FileOverHttp = function(filePath,req,res){
|
||||
var ext = filePath.split('.')
|
||||
ext = filePath[filePath.length - 1]
|
||||
ext = ext[ext.length - 1]
|
||||
var total = fs.statSync(filePath).size;
|
||||
if (req.headers['range']) {
|
||||
try{
|
||||
|
@ -392,15 +416,15 @@ module.exports = function(s,config,lang){
|
|||
var end = partialend ? parseInt(partialend, 10) : total-1;
|
||||
var chunksize = (end-start)+1;
|
||||
var file = fs.createReadStream(filePath, {start: start, end: end});
|
||||
req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext }
|
||||
req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+ext }
|
||||
req.writeCode=206
|
||||
}catch(err){
|
||||
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
|
||||
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
|
||||
var file = fs.createReadStream(filePath)
|
||||
req.writeCode=200
|
||||
}
|
||||
} else {
|
||||
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
|
||||
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
|
||||
var file = fs.createReadStream(filePath)
|
||||
req.writeCode=200
|
||||
}
|
||||
|
@ -414,4 +438,116 @@ module.exports = function(s,config,lang){
|
|||
file.pipe(res)
|
||||
return file
|
||||
}
|
||||
s.createVideoFromTimelapse = function(timelapseFrames,framesPerSecond,callback){
|
||||
framesPerSecond = parseInt(framesPerSecond)
|
||||
if(!framesPerSecond || isNaN(framesPerSecond))framesPerSecond = 2
|
||||
var frames = timelapseFrames.reverse()
|
||||
var ke = frames[0].ke
|
||||
var mid = frames[0].mid
|
||||
var finalFileName = frames[0].filename.split('.')[0] + '_' + frames[frames.length - 1].filename.split('.')[0] + `-${framesPerSecond}fps`
|
||||
var concatFiles = []
|
||||
var createLocation
|
||||
frames.forEach(function(frame,frameNumber){
|
||||
var selectedDate = frame.filename.split('T')[0]
|
||||
var fileLocationMid = `${frame.ke}/${frame.mid}_timelapse/${selectedDate}/`
|
||||
frame.details = s.parseJSON(frame.details)
|
||||
var fileLocation
|
||||
if(frame.details.dir){
|
||||
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
|
||||
}else{
|
||||
fileLocation = `${s.dir.videos}`
|
||||
}
|
||||
fileLocation = `${fileLocation}${fileLocationMid}${frame.filename}`
|
||||
concatFiles.push(fileLocation)
|
||||
if(frameNumber === 0){
|
||||
createLocation = fileLocationMid
|
||||
}
|
||||
})
|
||||
if(concatFiles.length > 30){
|
||||
var commandTempLocation = `${s.dir.streams}${ke}/${mid}/mergeJpegs_${finalFileName}.sh`
|
||||
var finalMp4OutputLocation = `${s.dir.fileBin}${ke}/${mid}/${finalFileName}.mp4`
|
||||
if(!s.group[ke].activeMonitors[mid].buildingTimelapseVideo){
|
||||
if(!fs.existsSync(finalMp4OutputLocation)){
|
||||
var currentFile = 0
|
||||
var completionTimeout
|
||||
var commandString = `ffmpeg -y -pattern_type glob -f image2pipe -vcodec mjpeg -r ${framesPerSecond} -analyzeduration 10 -i - -q:v 1 -c:v libx264 -r ${framesPerSecond} "${finalMp4OutputLocation}"`
|
||||
fs.writeFileSync(commandTempLocation,commandString)
|
||||
var videoBuildProcess = spawn('sh',[commandTempLocation])
|
||||
videoBuildProcess.stderr.on('data',function(data){
|
||||
// console.log(data.toString())
|
||||
clearTimeout(completionTimeout)
|
||||
completionTimeout = setTimeout(function(){
|
||||
if(currentFile === concatFiles.length - 1){
|
||||
videoBuildProcess.kill('SIGTERM')
|
||||
}
|
||||
},4000)
|
||||
})
|
||||
videoBuildProcess.on('exit',function(data){
|
||||
var timeNow = new Date()
|
||||
var fileStats = fs.statSync(finalMp4OutputLocation)
|
||||
var details = {}
|
||||
s.sqlQuery('INSERT INTO `Files` (ke,mid,details,name,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),finalFileName + '.mp4',fileStats.size,timeNow])
|
||||
s.setDiskUsedForGroup({ke: ke},fileStats.size / 1000000,'fileBin')
|
||||
fs.unlink(commandTempLocation,function(){
|
||||
|
||||
})
|
||||
delete(s.group[ke].activeMonitors[mid].buildingTimelapseVideo)
|
||||
})
|
||||
var readFile = function(){
|
||||
var filePath = concatFiles[currentFile]
|
||||
// console.log(filePath,currentFile,'/',concatFiles.length - 1)
|
||||
fs.readFile(filePath,function(err,buffer){
|
||||
if(!err)videoBuildProcess.stdin.write(buffer)
|
||||
if(currentFile === concatFiles.length - 1){
|
||||
//is last
|
||||
|
||||
}else{
|
||||
setTimeout(function(){
|
||||
++currentFile
|
||||
readFile()
|
||||
},1/framesPerSecond)
|
||||
}
|
||||
})
|
||||
}
|
||||
readFile()
|
||||
s.group[ke].activeMonitors[mid].buildingTimelapseVideo = videoBuildProcess
|
||||
callback({
|
||||
ok: true,
|
||||
fileExists: false,
|
||||
fileLocation: finalMp4OutputLocation,
|
||||
msg: lang['Started Building']
|
||||
})
|
||||
}else{
|
||||
callback({
|
||||
ok: false,
|
||||
fileExists: true,
|
||||
fileLocation: finalMp4OutputLocation,
|
||||
msg: lang['Already exists']
|
||||
})
|
||||
}
|
||||
}else{
|
||||
callback({
|
||||
ok: false,
|
||||
fileExists: false,
|
||||
fileLocation: finalMp4OutputLocation,
|
||||
msg: lang.Building
|
||||
})
|
||||
}
|
||||
}else{
|
||||
callback({
|
||||
ok: false,
|
||||
fileExists: false,
|
||||
msg: lang.notEnoughFramesText1
|
||||
})
|
||||
}
|
||||
}
|
||||
s.getVideoStorageIndex = function(video){
|
||||
var details = s.parseJSON(video.details) || {}
|
||||
var storageId = details.storageId
|
||||
if(s.group[video.ke] && s.group[video.ke].activeMonitors[video.id] && s.group[video.ke].activeMonitors[video.id].addStorageId)storageId = s.group[video.ke].activeMonitors[video.id].addStorageId
|
||||
if(storageId){
|
||||
return s.group[video.ke].addStorageUse[storageId]
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ module.exports = function(s,config,lang,io){
|
|||
if(config.renderPaths.dashcam === undefined){config.renderPaths.dashcam='pages/dashcam'}
|
||||
//embeddable widget page
|
||||
if(config.renderPaths.embed === undefined){config.renderPaths.embed='pages/embed'}
|
||||
//timelapse page (not modal)
|
||||
if(config.renderPaths.timelapse === undefined){config.renderPaths.timelapse='pages/timelapse'}
|
||||
//mjpeg full screen page
|
||||
if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
|
||||
//gridstack only page
|
||||
|
@ -53,6 +55,8 @@ module.exports = function(s,config,lang,io){
|
|||
// Use uws/cws
|
||||
if(config.useUWebsocketJs === undefined){config.useUWebsocketJs=true}
|
||||
//SSL options
|
||||
var wellKnownDirectory = s.mainDirectory + '/web/.well-known'
|
||||
if(fs.existsSync(wellKnownDirectory))app.use('/.well-known',express.static(wellKnownDirectory))
|
||||
if(config.ssl&&config.ssl.key&&config.ssl.cert){
|
||||
config.ssl.key=fs.readFileSync(s.checkRelativePath(config.ssl.key),'utf8')
|
||||
config.ssl.cert=fs.readFileSync(s.checkRelativePath(config.ssl.cert),'utf8')
|
||||
|
|
|
@ -68,7 +68,7 @@ module.exports = function(s,config,lang,app){
|
|||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
var form = s.getPostData(req)
|
||||
var form = s.getPostData(req) || {}
|
||||
var uid = form.uid || s.getPostData(req,'uid',false)
|
||||
var mail = form.mail || s.getPostData(req,'mail',false)
|
||||
s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[uid,req.params.ke,mail])
|
||||
|
@ -196,7 +196,7 @@ module.exports = function(s,config,lang,app){
|
|||
}
|
||||
}else{
|
||||
if(!user.details.sub || user.details.allmonitors === '1' || user.details.monitor_edit.indexOf(req.params.id) > -1 || hasRestrictions && user.details.monitor_create === '1'){
|
||||
s.userLog(s.group[req.params.ke].mon_conf[req.params.id],{type:'Monitor Deleted',msg:'by user : '+user.uid});
|
||||
s.userLog(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{type:'Monitor Deleted',msg:'by user : '+user.uid});
|
||||
req.params.delete=1;s.camera('stop',req.params);
|
||||
s.tx({f:'monitor_delete',uid:user.uid,mid:req.params.id,ke:req.params.ke},'GRP_'+req.params.ke);
|
||||
s.sqlQuery('DELETE FROM Monitors WHERE ke=? AND mid=?',[req.params.ke,req.params.id])
|
||||
|
|
|
@ -11,15 +11,16 @@ var httpProxy = require('http-proxy');
|
|||
var onvif = require('node-onvif');
|
||||
var proxy = httpProxy.createProxyServer({})
|
||||
var ejs = require('ejs');
|
||||
var CircularJSON = require('circular-json');
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
if(config.productType==='Pro'){
|
||||
var LdapAuth = require('ldapauth-fork');
|
||||
}
|
||||
s.renderPage = function(req,res,paths,passables,callback){
|
||||
passables.window = {}
|
||||
passables.data = req.params
|
||||
passables.originalURL = s.getOriginalUrl(req)
|
||||
passables.config = config
|
||||
passables.baseUrl = req.protocol+'://'+req.hostname
|
||||
passables.config = s.getConfigWithBranding(req.hostname)
|
||||
res.render(paths,passables,callback)
|
||||
}
|
||||
//child node proxy check
|
||||
|
@ -28,8 +29,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
//res = response, only needed for express (http server)
|
||||
//request = request, only needed for express (http server)
|
||||
s.checkChildProxy = function(params,cb,res,req){
|
||||
if(s.group[params.ke] && s.group[params.ke].mon[params.id] && s.group[params.ke].mon[params.id].childNode){
|
||||
var url = 'http://' + s.group[params.ke].mon[params.id].childNode// + req.originalUrl
|
||||
if(s.group[params.ke] && s.group[params.ke].activeMonitors[params.id] && s.group[params.ke].activeMonitors[params.id].childNode){
|
||||
var url = 'http://' + s.group[params.ke].activeMonitors[params.id].childNode// + req.originalUrl
|
||||
proxy.web(req, res, { target: url })
|
||||
}else{
|
||||
cb()
|
||||
|
@ -94,34 +95,22 @@ module.exports = function(s,config,lang,app,io){
|
|||
* Page : Login Screen
|
||||
*/
|
||||
app.get(config.webPaths.home, function (req,res){
|
||||
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'dashboard'},function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
})
|
||||
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'dashboard'})
|
||||
});
|
||||
/**
|
||||
* Page : Administrator Login Screen
|
||||
*/
|
||||
app.get(config.webPaths.admin, function (req,res){
|
||||
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'admin'},function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
})
|
||||
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'admin'})
|
||||
});
|
||||
/**
|
||||
* Page : Superuser Login Screen
|
||||
*/
|
||||
app.get(config.webPaths.super, function (req,res){
|
||||
|
||||
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'super'},function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
s.renderPage(req,res,config.renderPaths.index,{
|
||||
lang: lang,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
screen: 'super'
|
||||
})
|
||||
});
|
||||
/**
|
||||
|
@ -183,13 +172,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
failedLogin: true,
|
||||
message: lang.failedLoginText1,
|
||||
lang: s.copySystemDefaultLanguage(),
|
||||
config: config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
screen: screenChooser(req.params.screen)
|
||||
},function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
})
|
||||
}
|
||||
return false
|
||||
|
@ -207,12 +191,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
res.end(s.prettyPrint(data))
|
||||
}else{
|
||||
data.screen=req.params.screen
|
||||
s.renderPage(req,res,focus,data,function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
})
|
||||
s.renderPage(req,res,focus,data)
|
||||
}
|
||||
}
|
||||
failedAuthentication = function(board){
|
||||
|
@ -241,13 +220,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
failedLogin: true,
|
||||
message: lang.failedLoginText2,
|
||||
lang: s.copySystemDefaultLanguage(),
|
||||
config: config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
screen: screenChooser(req.params.screen)
|
||||
},function(err,html){
|
||||
if(err){
|
||||
s.systemLog(err)
|
||||
}
|
||||
res.end(html)
|
||||
})
|
||||
}
|
||||
var logTo = {
|
||||
|
@ -284,7 +258,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"dashcam"],function(err,rr){
|
||||
req.resp.mons=rr;
|
||||
renderPage(config.renderPaths.dashcam,{
|
||||
// config: config,
|
||||
// config: s.getConfigWithBranding(req.hostname),
|
||||
$user: req.resp,
|
||||
lang: r.lang,
|
||||
define: s.getDefinitonFile(r.details.lang),
|
||||
|
@ -296,7 +270,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"socket"],function(err,rr){
|
||||
req.resp.mons=rr;
|
||||
renderPage(config.renderPaths.streamer,{
|
||||
// config: config,
|
||||
// config: s.getConfigWithBranding(req.hostname),
|
||||
$user: req.resp,
|
||||
lang: r.lang,
|
||||
define: s.getDefinitonFile(r.details.lang),
|
||||
|
@ -309,7 +283,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.sqlQuery('SELECT uid,mail,details FROM Users WHERE ke=? AND details LIKE \'%"sub"%\'',[r.ke],function(err,rr) {
|
||||
s.sqlQuery('SELECT * FROM Monitors WHERE ke=?',[r.ke],function(err,rrr) {
|
||||
renderPage(config.renderPaths.admin,{
|
||||
config: config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
$user: req.resp,
|
||||
$subs: rr,
|
||||
$mons: rrr,
|
||||
|
@ -321,9 +295,13 @@ module.exports = function(s,config,lang,app,io){
|
|||
})
|
||||
}else{
|
||||
//not admin user
|
||||
renderPage(config.renderPaths.home,{
|
||||
var chosenRender = 'home'
|
||||
if(r.details.landing_page && r.details.landing_page !== '' && config.renderPaths[r.details.landing_page]){
|
||||
chosenRender = r.details.landing_page
|
||||
}
|
||||
renderPage(config.renderPaths[chosenRender],{
|
||||
$user:req.resp,
|
||||
config:config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
lang:r.lang,
|
||||
define:s.getDefinitonFile(r.details.lang),
|
||||
addStorage:s.dir.addStorage,
|
||||
|
@ -334,16 +312,20 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
break;
|
||||
default:
|
||||
renderPage(config.renderPaths.home,{
|
||||
var chosenRender = 'home'
|
||||
if(r.details.sub && r.details.landing_page && r.details.landing_page !== '' && config.renderPaths[r.details.landing_page]){
|
||||
chosenRender = r.details.landing_page
|
||||
}
|
||||
renderPage(config.renderPaths[chosenRender],{
|
||||
$user:req.resp,
|
||||
config:config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
lang:r.lang,
|
||||
define:s.getDefinitonFile(r.details.lang),
|
||||
addStorage:s.dir.addStorage,
|
||||
fs:fs,
|
||||
__dirname:s.mainDirectory,
|
||||
customAutoLoad: s.customAutoLoadTree
|
||||
});
|
||||
})
|
||||
break;
|
||||
}
|
||||
s.userLog({ke:r.ke,mid:'$USER'},{type:r.lang['New Authentication Token'],msg:{for:req.body.function,mail:r.mail,id:r.uid,ip:req.ip}})
|
||||
|
@ -360,6 +342,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
r.details=JSON.parse(r.details);
|
||||
r.lang=s.getLanguageFile(r.details.lang)
|
||||
req.factorAuth=function(cb){
|
||||
req.params.auth = r.auth
|
||||
req.params.ke = r.ke
|
||||
if(r.details.factorAuth === "1"){
|
||||
if(!r.details.acceptedMachines||!(r.details.acceptedMachines instanceof Object)){
|
||||
r.details.acceptedMachines={}
|
||||
|
@ -494,8 +478,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
req.resp.lang=r.lang
|
||||
s.sqlQuery('INSERT INTO Users (ke,uid,auth,mail,pass,details) VALUES (?,?,?,?,?,?)',user.post)
|
||||
}
|
||||
req.resp.details=JSON.stringify(req.resp.details)
|
||||
req.resp.auth_token=req.resp.auth
|
||||
req.resp.details = JSON.stringify(req.resp.details)
|
||||
req.resp.auth_token = req.resp.auth
|
||||
req.resp.ok=true
|
||||
checkRoute(req.resp)
|
||||
})
|
||||
|
@ -534,6 +518,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
data.Logs = r
|
||||
data.customAutoLoad = s.customAutoLoadTree
|
||||
data.currentVersion = s.currentVersion
|
||||
fs.readFile(s.location.config,'utf8',function(err,file){
|
||||
data.plainConfig = JSON.parse(file)
|
||||
renderPage(config.renderPaths.super,data)
|
||||
|
@ -644,8 +629,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
r = filteredByGroup;
|
||||
}
|
||||
r.forEach(function(v,n){
|
||||
if(s.group[v.ke]&&s.group[v.ke].mon[v.mid]&&s.group[v.ke].mon[v.mid].watch){
|
||||
r[n].currentlyWatching=Object.keys(s.group[v.ke].mon[v.mid].watch).length
|
||||
if(s.group[v.ke]&&s.group[v.ke].activeMonitors[v.mid]&&s.group[v.ke].activeMonitors[v.mid].watch){
|
||||
r[n].currentlyWatching=Object.keys(s.group[v.ke].activeMonitors[v.mid].watch).length
|
||||
}
|
||||
r[n].subStream={}
|
||||
var details = JSON.parse(r[n].details)
|
||||
|
@ -689,7 +674,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.renderPage(req,res,page,{
|
||||
data:Object.assign(req.params,req.query),
|
||||
baseUrl:req.protocol+'://'+req.hostname,
|
||||
config:config,
|
||||
config: s.getConfigWithBranding(req.hostname),
|
||||
lang:user.lang,
|
||||
$user:user,
|
||||
monitors:r,
|
||||
|
@ -866,10 +851,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
r.forEach(function(v,n){
|
||||
if(s.group[v.ke] && s.group[v.ke].mon[v.mid]){
|
||||
r[n].currentlyWatching = Object.keys(s.group[v.ke].mon[v.mid].watch).length
|
||||
r[n].currentCpuUsage = s.group[v.ke].mon[v.mid].currentCpuUsage
|
||||
r[n].status = s.group[v.ke].mon[v.mid].monitorStatus
|
||||
if(s.group[v.ke] && s.group[v.ke].activeMonitors[v.mid]){
|
||||
r[n].currentlyWatching = Object.keys(s.group[v.ke].activeMonitors[v.mid].watch).length
|
||||
r[n].currentCpuUsage = s.group[v.ke].activeMonitors[v.mid].currentCpuUsage
|
||||
r[n].status = s.group[v.ke].activeMonitors[v.mid].monitorStatus
|
||||
}
|
||||
var buildStreamURL = function(type,channelNumber){
|
||||
var streamURL
|
||||
|
@ -909,7 +894,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
r[n].streamsSortedByType={}
|
||||
buildStreamURL(details.stream_type)
|
||||
if(details.stream_channels&&details.stream_channels!==''){
|
||||
details.stream_channels=JSON.parse(details.stream_channels)
|
||||
details.stream_channels=s.parseJSON(details.stream_channels)
|
||||
details.stream_channels.forEach(function(b,m){
|
||||
buildStreamURL(b.stream_type,m.toString())
|
||||
})
|
||||
|
@ -1096,7 +1081,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
/**
|
||||
* API : Get Events
|
||||
*/
|
||||
app.get([config.webPaths.apiPrefix+':auth/events/:ke',config.webPaths.apiPrefix+':auth/events/:ke/:id',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start/:end'], function (req,res){
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/events/:ke',
|
||||
config.webPaths.apiPrefix+':auth/events/:ke/:id'
|
||||
], function (req,res){
|
||||
req.ret={ok:false};
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
|
@ -1122,20 +1110,42 @@ module.exports = function(s,config,lang,app,io){
|
|||
return;
|
||||
}
|
||||
}
|
||||
if(req.params.start&&req.params.start!==''){
|
||||
req.params.start = s.stringToSqlTime(req.params.start)
|
||||
if(req.params.end&&req.params.end!==''){
|
||||
req.params.end = s.stringToSqlTime(req.params.end)
|
||||
req.sql+=' AND `time` >= ? AND `time` <= ?';
|
||||
req.ar.push(decodeURIComponent(req.params.start))
|
||||
req.ar.push(decodeURIComponent(req.params.end))
|
||||
}else{
|
||||
req.sql+=' AND `time` >= ?';
|
||||
req.ar.push(decodeURIComponent(req.params.start))
|
||||
if(req.query.start||req.query.end){
|
||||
if(req.query.start && req.query.start !== ''){
|
||||
req.query.start = s.stringToSqlTime(req.query.start)
|
||||
}
|
||||
if(req.query.end && req.query.end !== ''){
|
||||
req.query.end = s.stringToSqlTime(req.query.end)
|
||||
}
|
||||
if(!req.query.startOperator||req.query.startOperator==''){
|
||||
req.query.startOperator='>='
|
||||
}
|
||||
if(!req.query.endOperator||req.query.endOperator==''){
|
||||
req.query.endOperator='<='
|
||||
}
|
||||
switch(true){
|
||||
case(req.query.start&&req.query.start!==''&&req.query.end&&req.query.end!==''):
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `time` '+req.query.endOperator+' ?';
|
||||
req.ar.push(req.query.start)
|
||||
req.ar.push(req.query.end)
|
||||
break;
|
||||
case(req.query.start&&req.query.start!==''):
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ?';
|
||||
req.ar.push(req.query.start)
|
||||
break;
|
||||
case(req.query.end&&req.query.end!==''):
|
||||
req.sql+=' AND `time` '+req.query.endOperator+' ?';
|
||||
req.ar.push(req.query.end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!req.params.limit||req.params.limit==''){req.params.limit=100}
|
||||
req.sql+=' ORDER BY `time` DESC LIMIT '+req.params.limit+'';
|
||||
req.sql+=' ORDER BY `time` DESC';
|
||||
if(!req.query.limit||req.query.limit==''){
|
||||
req.query.limit='100'
|
||||
}
|
||||
if(req.query.limit!=='0'){
|
||||
req.sql+=' LIMIT '+req.query.limit
|
||||
}
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
if(err){
|
||||
err.sql=req.sql;
|
||||
|
@ -1238,7 +1248,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
if(r&&r[0]){
|
||||
req.ar=[];
|
||||
r.forEach(function(v){
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[v.mid]&&s.group[req.params.ke].mon[v.mid].isStarted === true){
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[v.mid]&&s.group[req.params.ke].activeMonitors[v.mid].isStarted === true){
|
||||
req.ar.push(v)
|
||||
}
|
||||
})
|
||||
|
@ -1270,25 +1280,25 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[req.params.ke,req.params.id],function(err,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0];
|
||||
if(req.query.reset==='1'||(s.group[r.ke]&&s.group[r.ke].mon_conf[r.mid].mode!==req.params.f)||req.query.fps&&(!s.group[r.ke].mon[r.mid].currentState||!s.group[r.ke].mon[r.mid].currentState.trigger_on)){
|
||||
if(req.query.reset!=='1'||!s.group[r.ke].mon[r.mid].trigger_timer){
|
||||
if(!s.group[r.ke].mon[r.mid].currentState)s.group[r.ke].mon[r.mid].currentState={}
|
||||
s.group[r.ke].mon[r.mid].currentState.mode=r.mode.toString()
|
||||
s.group[r.ke].mon[r.mid].currentState.fps=r.fps.toString()
|
||||
if(!s.group[r.ke].mon[r.mid].currentState.trigger_on){
|
||||
s.group[r.ke].mon[r.mid].currentState.trigger_on=true
|
||||
if(req.query.reset==='1'||(s.group[r.ke]&&s.group[r.ke].rawMonitorConfigurations[r.mid].mode!==req.params.f)||req.query.fps&&(!s.group[r.ke].activeMonitors[r.mid].currentState||!s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on)){
|
||||
if(req.query.reset!=='1'||!s.group[r.ke].activeMonitors[r.mid].trigger_timer){
|
||||
if(!s.group[r.ke].activeMonitors[r.mid].currentState)s.group[r.ke].activeMonitors[r.mid].currentState={}
|
||||
s.group[r.ke].activeMonitors[r.mid].currentState.mode=r.mode.toString()
|
||||
s.group[r.ke].activeMonitors[r.mid].currentState.fps=r.fps.toString()
|
||||
if(!s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on){
|
||||
s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on=true
|
||||
}else{
|
||||
s.group[r.ke].mon[r.mid].currentState.trigger_on=false
|
||||
s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on=false
|
||||
}
|
||||
r.mode=req.params.f;
|
||||
try{r.details=JSON.parse(r.details);}catch(er){}
|
||||
if(req.query.fps){
|
||||
r.fps=parseFloat(r.details.detector_trigger_record_fps)
|
||||
s.group[r.ke].mon[r.mid].currentState.detector_trigger_record_fps=r.fps
|
||||
s.group[r.ke].activeMonitors[r.mid].currentState.detector_trigger_record_fps=r.fps
|
||||
}
|
||||
r.id=r.mid;
|
||||
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[r.mode,r.ke,r.mid]);
|
||||
s.group[r.ke].mon_conf[r.mid]=r;
|
||||
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
|
||||
s.camera('stop',s.cleanMonitorObject(r));
|
||||
|
@ -1303,7 +1313,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
req.ret.ok=true;
|
||||
if(req.params.ff&&req.params.f!=='stop'){
|
||||
req.params.ff=parseFloat(req.params.ff);
|
||||
clearTimeout(s.group[r.ke].mon[r.mid].trigger_timer)
|
||||
clearTimeout(s.group[r.ke].activeMonitors[r.mid].trigger_timer)
|
||||
switch(req.params.fff){
|
||||
case'day':case'days':
|
||||
req.timeout=req.params.ff*1000*60*60*24
|
||||
|
@ -1318,17 +1328,17 @@ module.exports = function(s,config,lang,app,io){
|
|||
req.timeout=req.params.ff*1000
|
||||
break;
|
||||
}
|
||||
s.group[r.ke].mon[r.mid].trigger_timer=setTimeout(function(){
|
||||
delete(s.group[r.ke].mon[r.mid].trigger_timer)
|
||||
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[s.group[r.ke].mon[r.mid].currentState.mode,r.ke,r.mid]);
|
||||
s.group[r.ke].activeMonitors[r.mid].trigger_timer=setTimeout(function(){
|
||||
delete(s.group[r.ke].activeMonitors[r.mid].trigger_timer)
|
||||
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[s.group[r.ke].activeMonitors[r.mid].currentState.mode,r.ke,r.mid]);
|
||||
r.neglectTriggerTimer=1;
|
||||
r.mode=s.group[r.ke].mon[r.mid].currentState.mode;
|
||||
r.fps=s.group[r.ke].mon[r.mid].currentState.fps;
|
||||
r.mode=s.group[r.ke].activeMonitors[r.mid].currentState.mode;
|
||||
r.fps=s.group[r.ke].activeMonitors[r.mid].currentState.fps;
|
||||
s.camera('stop',s.cleanMonitorObject(r),function(){
|
||||
if(s.group[r.ke].mon[r.mid].currentState.mode!=='stop'){
|
||||
s.camera(s.group[r.ke].mon[r.mid].currentState.mode,s.cleanMonitorObject(r));
|
||||
if(s.group[r.ke].activeMonitors[r.mid].currentState.mode!=='stop'){
|
||||
s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
|
||||
}
|
||||
s.group[r.ke].mon_conf[r.mid]=r;
|
||||
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
|
||||
});
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
|
||||
|
@ -1382,31 +1392,32 @@ module.exports = function(s,config,lang,app,io){
|
|||
* API : Get fileBin file
|
||||
*/
|
||||
app.get(config.webPaths.apiPrefix+':auth/fileBin/:ke/:id/:year/:month/:day/:file', function (req,res){
|
||||
req.fn=function(user){
|
||||
req.failed=function(){
|
||||
s.auth(req.params,function(user){
|
||||
var failed = function(){
|
||||
res.end(user.lang['File Not Found'])
|
||||
}
|
||||
if (!s.group[req.params.ke].fileBin[req.params.id+'/'+req.params.file]){
|
||||
s.sqlQuery('SELECT * FROM Files WHERE ke=? AND mid=? AND name=?',[req.params.ke,req.params.id,req.params.file],function(err,r){
|
||||
if(r&&r[0]){
|
||||
r=r[0]
|
||||
r.details=JSON.parse(r.details)
|
||||
req.dir=s.dir.fileBin+req.params.ke+'/'+req.params.id+'/'+r.details.year+'/'+r.details.month+'/'+r.details.day+'/'+req.params.file;
|
||||
if(fs.existsSync(req.dir)){
|
||||
res.on('finish',function(){res.end();});
|
||||
fs.createReadStream(req.dir).pipe(res);
|
||||
}else{
|
||||
req.failed()
|
||||
}
|
||||
r = r[0]
|
||||
r.details = JSON.parse(r.details)
|
||||
req.dir = s.dir.fileBin+req.params.ke+'/'+req.params.id+'/'+r.details.year+'/'+r.details.month+'/'+r.details.day+'/'+req.params.file;
|
||||
fs.stat(req.dir,function(err,stats){
|
||||
if(!err){
|
||||
res.on('finish',function(){res.end()})
|
||||
fs.createReadStream(req.dir).pipe(res)
|
||||
}else{
|
||||
failed()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
req.failed()
|
||||
failed()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
res.end(user.lang['Please Wait for Completion'])
|
||||
}
|
||||
}
|
||||
s.auth(req.params,req.fn,res,req);
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Zip Videos and Get Link from fileBin
|
||||
|
@ -1451,52 +1462,52 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
fs.unlink(zippedFile);
|
||||
})
|
||||
if(!fs.existsSync(fileBinDir)){
|
||||
fs.mkdirSync(fileBinDir);
|
||||
}
|
||||
r.forEach(function(video){
|
||||
var timeFormatted = s.formattedTime(video.time)
|
||||
video.filename = timeFormatted+'.'+video.ext
|
||||
var dir = s.getVideoDirectory(video)+video.filename
|
||||
var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext
|
||||
fs.writeFileSync(fileBinDir+tempVideoFile, fs.readFileSync(dir))
|
||||
tempFiles.push(fileBinDir+tempVideoFile)
|
||||
script += ' "'+tempVideoFile+'"'
|
||||
})
|
||||
fs.writeFileSync(tempScript,script,'utf8')
|
||||
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
|
||||
zipCreate.stderr.on('data',function(data){
|
||||
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
|
||||
})
|
||||
zipCreate.on('exit',function(data){
|
||||
fs.unlinkSync(tempScript)
|
||||
tempFiles.forEach(function(file){
|
||||
fs.unlink(file,function(){})
|
||||
fs.mkdir(fileBinDir,function(err){
|
||||
s.handleFolderError(err)
|
||||
r.forEach(function(video){
|
||||
var timeFormatted = s.formattedTime(video.time)
|
||||
video.filename = timeFormatted+'.'+video.ext
|
||||
var dir = s.getVideoDirectory(video)+video.filename
|
||||
var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext
|
||||
fs.writeFileSync(fileBinDir+tempVideoFile, fs.readFileSync(dir))
|
||||
tempFiles.push(fileBinDir+tempVideoFile)
|
||||
script += ' "'+tempVideoFile+'"'
|
||||
})
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="'+zippedFilename+'"')
|
||||
var zipDownload = fs.createReadStream(zippedFile)
|
||||
zipDownload.pipe(res)
|
||||
zipDownload.on('error', function (error) {
|
||||
var errorString = error.toString()
|
||||
s.userLog({
|
||||
ke: req.params.ke,
|
||||
mid: '$USER'
|
||||
},{
|
||||
title: 'Zip Download Error',
|
||||
msg: errorString
|
||||
fs.writeFileSync(tempScript,script,'utf8')
|
||||
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
|
||||
zipCreate.stderr.on('data',function(data){
|
||||
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
|
||||
})
|
||||
zipCreate.on('exit',function(data){
|
||||
fs.unlinkSync(tempScript)
|
||||
tempFiles.forEach(function(file){
|
||||
fs.unlink(file,function(){})
|
||||
})
|
||||
if(zipDownload && zipDownload.destroy){
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="'+zippedFilename+'"')
|
||||
var zipDownload = fs.createReadStream(zippedFile)
|
||||
zipDownload.pipe(res)
|
||||
zipDownload.on('error', function (error) {
|
||||
var errorString = error.toString()
|
||||
s.userLog({
|
||||
ke: req.params.ke,
|
||||
mid: '$USER'
|
||||
},{
|
||||
title: 'Zip Download Error',
|
||||
msg: errorString
|
||||
})
|
||||
if(zipDownload && zipDownload.destroy){
|
||||
zipDownload.destroy()
|
||||
}
|
||||
res.end(s.prettyPrint({
|
||||
ok: false,
|
||||
msg: errorString
|
||||
}))
|
||||
})
|
||||
zipDownload.on('close', function () {
|
||||
res.end()
|
||||
zipDownload.destroy()
|
||||
}
|
||||
res.end(s.prettyPrint({
|
||||
ok: false,
|
||||
msg: errorString
|
||||
}))
|
||||
})
|
||||
zipDownload.on('close', function () {
|
||||
res.end()
|
||||
zipDownload.destroy()
|
||||
fs.unlinkSync(zippedFile)
|
||||
fs.unlinkSync(zippedFile)
|
||||
})
|
||||
})
|
||||
})
|
||||
}else{
|
||||
|
@ -1551,64 +1562,63 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
fs.unlink(zippedFile);
|
||||
})
|
||||
if(!fs.existsSync(fileBinDir)){
|
||||
fs.mkdirSync(fileBinDir);
|
||||
}
|
||||
var cloudDownloadCount = 0
|
||||
var getFile = function(video,completed){
|
||||
if(!video)completed();
|
||||
s.checkDetails(video)
|
||||
var filename = video.href.split('/')
|
||||
filename = filename[filename.length - 1]
|
||||
var timeFormatted = s.formattedTime(video.time)
|
||||
var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename
|
||||
var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile)
|
||||
tempFileWriteStream.on('finish', function() {
|
||||
++cloudDownloadCount
|
||||
getFile(r[cloudDownloadCount],completed)
|
||||
})
|
||||
var cloudVideoDownload = request(video.href)
|
||||
cloudVideoDownload.on('response', function (res) {
|
||||
res.pipe(tempFileWriteStream)
|
||||
})
|
||||
tempFiles.push(fileBinDir+tempVideoFile)
|
||||
script += ' "'+tempVideoFile+'"'
|
||||
}
|
||||
getFile(r[cloudDownloadCount],function(){
|
||||
fs.writeFileSync(tempScript,script,'utf8')
|
||||
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
|
||||
zipCreate.stderr.on('data',function(data){
|
||||
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
|
||||
})
|
||||
zipCreate.on('exit',function(data){
|
||||
fs.unlinkSync(tempScript)
|
||||
tempFiles.forEach(function(file){
|
||||
fs.unlink(file,function(){})
|
||||
fs.mkdir(fileBinDir,function(err){
|
||||
var cloudDownloadCount = 0
|
||||
var getFile = function(video,completed){
|
||||
if(!video)completed();
|
||||
s.checkDetails(video)
|
||||
var filename = video.href.split('/')
|
||||
filename = filename[filename.length - 1]
|
||||
var timeFormatted = s.formattedTime(video.time)
|
||||
var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename
|
||||
var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile)
|
||||
tempFileWriteStream.on('finish', function() {
|
||||
++cloudDownloadCount
|
||||
getFile(r[cloudDownloadCount],completed)
|
||||
})
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"')
|
||||
var zipDownload = fs.createReadStream(zippedFile)
|
||||
zipDownload.pipe(res)
|
||||
zipDownload.on('error', function (error) {
|
||||
var errorString = error.toString()
|
||||
s.userLog({
|
||||
ke: req.params.ke,
|
||||
mid: '$USER'
|
||||
},{
|
||||
title: 'Zip Download Error',
|
||||
msg: errorString
|
||||
var cloudVideoDownload = request(video.href)
|
||||
cloudVideoDownload.on('response', function (res) {
|
||||
res.pipe(tempFileWriteStream)
|
||||
})
|
||||
tempFiles.push(fileBinDir+tempVideoFile)
|
||||
script += ' "'+tempVideoFile+'"'
|
||||
}
|
||||
getFile(r[cloudDownloadCount],function(){
|
||||
fs.writeFileSync(tempScript,script,'utf8')
|
||||
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
|
||||
zipCreate.stderr.on('data',function(data){
|
||||
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
|
||||
})
|
||||
zipCreate.on('exit',function(data){
|
||||
fs.unlinkSync(tempScript)
|
||||
tempFiles.forEach(function(file){
|
||||
fs.unlink(file,function(){})
|
||||
})
|
||||
if(zipDownload && zipDownload.destroy){
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"')
|
||||
var zipDownload = fs.createReadStream(zippedFile)
|
||||
zipDownload.pipe(res)
|
||||
zipDownload.on('error', function (error) {
|
||||
var errorString = error.toString()
|
||||
s.userLog({
|
||||
ke: req.params.ke,
|
||||
mid: '$USER'
|
||||
},{
|
||||
title: 'Zip Download Error',
|
||||
msg: errorString
|
||||
})
|
||||
if(zipDownload && zipDownload.destroy){
|
||||
zipDownload.destroy()
|
||||
}
|
||||
res.end(s.prettyPrint({
|
||||
ok: false,
|
||||
msg: errorString
|
||||
}))
|
||||
})
|
||||
zipDownload.on('close', function () {
|
||||
res.end()
|
||||
zipDownload.destroy()
|
||||
}
|
||||
res.end(s.prettyPrint({
|
||||
ok: false,
|
||||
msg: errorString
|
||||
}))
|
||||
})
|
||||
zipDownload.on('close', function () {
|
||||
res.end()
|
||||
zipDownload.destroy()
|
||||
fs.unlinkSync(zippedFile)
|
||||
fs.unlinkSync(zippedFile)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1662,11 +1672,13 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND mid=? AND `time`=? LIMIT 1',[req.params.ke,req.params.id,time],function(err,r){
|
||||
if(r&&r[0]){
|
||||
req.dir=s.getVideoDirectory(r[0])+req.params.file
|
||||
if (fs.existsSync(req.dir)){
|
||||
s.streamMp4FileOverHttp(req.dir,req,res)
|
||||
}else{
|
||||
res.end(user.lang['File Not Found in Filesystem'])
|
||||
}
|
||||
fs.stat(req.dir,function(err,stats){
|
||||
if (!err){
|
||||
s.streamMp4FileOverHttp(req.dir,req,res)
|
||||
}else{
|
||||
res.end(user.lang['File Not Found in Filesystem'])
|
||||
}
|
||||
})
|
||||
}else{
|
||||
res.end(user.lang['File Not Found in Database'])
|
||||
}
|
||||
|
@ -1678,30 +1690,76 @@ module.exports = function(s,config,lang,app,io){
|
|||
*/
|
||||
app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){
|
||||
s.auth(req.params,function(user){
|
||||
var endData = {
|
||||
|
||||
}
|
||||
if(req.query.data){
|
||||
try{
|
||||
var d = {
|
||||
id: req.params.id,
|
||||
ke: req.params.ke,
|
||||
details: JSON.parse(req.query.data)
|
||||
details: s.parseJSON(req.query.data)
|
||||
}
|
||||
}catch(err){
|
||||
res.end('Data Broken',err)
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['Data Broken']
|
||||
})
|
||||
return
|
||||
}
|
||||
}else{
|
||||
res.end('No Data')
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['No Data']
|
||||
})
|
||||
return
|
||||
}
|
||||
if(!d.ke||!d.id||!s.group[d.ke]){
|
||||
res.end(user.lang['No Group with this key exists'])
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['No Group with this key exists']
|
||||
})
|
||||
return
|
||||
}
|
||||
if(!s.group[d.ke].rawMonitorConfigurations[d.id]){
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['Monitor or Key does not exist.']
|
||||
})
|
||||
return
|
||||
}
|
||||
var details = s.group[d.ke].rawMonitorConfigurations[d.id].details
|
||||
var detectorHttpApi = details.detector_http_api
|
||||
var detectorOn = (details.detector === '1')
|
||||
switch(detectorHttpApi){
|
||||
case'0':
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['Trigger Blocked']
|
||||
})
|
||||
return
|
||||
break;
|
||||
case'2':
|
||||
if(!detectorOn){
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['Trigger Blocked']
|
||||
})
|
||||
return
|
||||
}
|
||||
break;
|
||||
case'2':
|
||||
if(detectorOn){
|
||||
s.closeJsonResponse(res,{
|
||||
ok: false,
|
||||
msg: user.lang['Trigger Blocked']
|
||||
})
|
||||
return
|
||||
}
|
||||
break;
|
||||
}
|
||||
s.triggerEvent(d)
|
||||
res.end(user.lang['Trigger Successful'])
|
||||
s.closeJsonResponse(res,{
|
||||
ok: true,
|
||||
msg: user.lang['Trigger Successful']
|
||||
})
|
||||
},res,req)
|
||||
})
|
||||
/**
|
||||
|
@ -1809,13 +1867,13 @@ module.exports = function(s,config,lang,app,io){
|
|||
var checkOrigin = function(search){return req.headers.host.indexOf(search)>-1}
|
||||
if(checkOrigin('127.0.0.1')){
|
||||
if(!req.params.feed){req.params.feed='1'}
|
||||
if(!s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed]){
|
||||
s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed] = new events.EventEmitter().setMaxListeners(0)
|
||||
if(!s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed]){
|
||||
s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed] = new events.EventEmitter().setMaxListeners(0)
|
||||
}
|
||||
//req.params.feed = Feed Number
|
||||
res.connection.setTimeout(0);
|
||||
req.on('data', function(buffer){
|
||||
s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed].emit('data',buffer)
|
||||
s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed].emit('data',buffer)
|
||||
});
|
||||
req.on('end',function(){
|
||||
// console.log('streamIn closed',req.params);
|
||||
|
@ -1873,7 +1931,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
/**
|
||||
* API : ONVIF Method Controller
|
||||
*/
|
||||
app.all([config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'],function (req,res){
|
||||
app.all([
|
||||
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',
|
||||
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'
|
||||
],function (req,res){
|
||||
var response = {ok:false};
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
|
@ -1904,7 +1965,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
var completeAction = function(command){
|
||||
if(command.then){
|
||||
command.then(actionCallback).catch(function(error){
|
||||
errorMessage('Device responded with an error',error)
|
||||
errorMessage('Device Action responded with an error',error)
|
||||
})
|
||||
}else if(command){
|
||||
response.ok = true
|
||||
|
@ -1927,6 +1988,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
}else{
|
||||
action = Camera[req.params.action]
|
||||
}
|
||||
console.log(s.parseJSON(req.query.options))
|
||||
if(!action || typeof action !== 'function'){
|
||||
errorMessage(req.params.action+' is not an available ONVIF function. See https://github.com/futomi/node-onvif for functions.')
|
||||
}else{
|
||||
|
@ -1970,10 +2032,10 @@ module.exports = function(s,config,lang,app,io){
|
|||
completeAction(command)
|
||||
}
|
||||
}
|
||||
if(!s.group[req.params.ke].mon[req.params.id].onvifConnection){
|
||||
if(!s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection){
|
||||
//prepeare onvif connection
|
||||
var controlURL
|
||||
var monitorConfig = s.group[req.params.ke].mon_conf[req.params.id]
|
||||
var monitorConfig = s.group[req.params.ke].rawMonitorConfigurations[req.params.id]
|
||||
if(!monitorConfig.details.control_base_url||monitorConfig.details.control_base_url===''){
|
||||
controlURL = s.buildMonitorUrl(monitorConfig, true)
|
||||
}else{
|
||||
|
@ -1981,19 +2043,19 @@ module.exports = function(s,config,lang,app,io){
|
|||
}
|
||||
var controlURLOptions = s.cameraControlOptionsFromUrl(controlURL,monitorConfig)
|
||||
//create onvif connection
|
||||
s.group[req.params.ke].mon[req.params.id].onvifConnection = new onvif.OnvifDevice({
|
||||
s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection = new onvif.OnvifDevice({
|
||||
xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service',
|
||||
user : controlURLOptions.username,
|
||||
pass : controlURLOptions.password
|
||||
})
|
||||
var device = s.group[req.params.ke].mon[req.params.id].onvifConnection
|
||||
var device = s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection
|
||||
device.init().then((info) => {
|
||||
if(info)doAction(device)
|
||||
}).catch(function(error){
|
||||
return errorMessage('Device responded with an error',error)
|
||||
})
|
||||
}else{
|
||||
doAction(s.group[req.params.ke].mon[req.params.id].onvifConnection)
|
||||
doAction(s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection)
|
||||
}
|
||||
},res,req);
|
||||
})
|
||||
|
@ -2021,6 +2083,30 @@ module.exports = function(s,config,lang,app,io){
|
|||
},res,req)
|
||||
})
|
||||
/**
|
||||
* API : Get Definitions JSON
|
||||
*/
|
||||
app.get(config.webPaths.apiPrefix+':auth/definitions/:ke',function (req,res){
|
||||
s.auth(req.params,function(user){
|
||||
var endData = {
|
||||
ok: true,
|
||||
definitions: s.getDefinitonFile(user.details.lang)
|
||||
}
|
||||
s.closeJsonResponse(res,endData)
|
||||
},res,req)
|
||||
})
|
||||
/**
|
||||
* API : Get Language JSON
|
||||
*/
|
||||
app.get(config.webPaths.apiPrefix+':auth/language/:ke',function (req,res){
|
||||
s.auth(req.params,function(user){
|
||||
var endData = {
|
||||
ok: true,
|
||||
definitions: s.getLanguageFile(user.details.lang)
|
||||
}
|
||||
s.closeJsonResponse(res,endData)
|
||||
},res,req)
|
||||
})
|
||||
/**
|
||||
* Robots.txt
|
||||
*/
|
||||
app.get('/robots.txt', function (req,res){
|
||||
|
|
|
@ -10,7 +10,6 @@ var spawn = require('child_process').spawn;
|
|||
var httpProxy = require('http-proxy');
|
||||
var proxy = httpProxy.createProxyServer({})
|
||||
var ejs = require('ejs');
|
||||
var CircularJSON = require('circular-json');
|
||||
module.exports = function(s,config,lang,app){
|
||||
/**
|
||||
* Page : Get Embed Stream
|
||||
|
@ -22,10 +21,10 @@ module.exports = function(s,config,lang,app){
|
|||
res.end(user.lang['Not Permitted'])
|
||||
return
|
||||
}
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[req.params.id]){
|
||||
if(s.group[req.params.ke].mon[req.params.id].isStarted === true){
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[req.params.id]){
|
||||
if(s.group[req.params.ke].activeMonitors[req.params.id].isStarted === true){
|
||||
req.params.uid=user.uid;
|
||||
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config:config,lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].mon_conf[req.params.id])),originalURL:s.getOriginalUrl(req)});
|
||||
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config: s.getConfigWithBranding(req.hostname),lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].rawMonitorConfigurations[req.params.id])),originalURL:s.getOriginalUrl(req)});
|
||||
res.end()
|
||||
}else{
|
||||
res.end(user.lang['Cannot watch a monitor that isn\'t running.'])
|
||||
|
@ -40,7 +39,7 @@ module.exports = function(s,config,lang,app){
|
|||
*/
|
||||
app.get([config.webPaths.apiPrefix+':auth/mp4/:ke/:id/:channel/s.mp4',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/s.mp4',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/:channel/s.ts',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/s.ts'], function (req, res) {
|
||||
s.auth(req.params,function(user){
|
||||
if(!s.group[req.params.ke] || !s.group[req.params.ke].mon[req.params.id]){
|
||||
if(!s.group[req.params.ke] || !s.group[req.params.ke].activeMonitors[req.params.id]){
|
||||
res.status(404);
|
||||
res.end('404 : Monitor not found');
|
||||
return
|
||||
|
@ -50,7 +49,7 @@ module.exports = function(s,config,lang,app){
|
|||
if(req.params.channel){
|
||||
Channel = parseInt(req.params.channel)+config.pipeAddition
|
||||
}
|
||||
var mp4frag = s.group[req.params.ke].mon[req.params.id].mp4frag[Channel];
|
||||
var mp4frag = s.group[req.params.ke].activeMonitors[req.params.id].mp4frag[Channel];
|
||||
var errorMessage = 'MP4 Stream is not enabled'
|
||||
if(!mp4frag){
|
||||
res.status(503);
|
||||
|
@ -105,7 +104,7 @@ module.exports = function(s,config,lang,app){
|
|||
}else{
|
||||
s.auth(req.params,function(user){
|
||||
s.checkChildProxy(req.params,function(){
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[req.params.id]){
|
||||
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[req.params.id]){
|
||||
if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
|
||||
res.end(user.lang['Not Permitted'])
|
||||
return
|
||||
|
@ -113,9 +112,9 @@ module.exports = function(s,config,lang,app){
|
|||
|
||||
var Emitter
|
||||
if(!req.params.channel){
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
|
||||
}else{
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
}
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'multipart/x-mixed-replace; boundary=shinobi',
|
||||
|
@ -123,33 +122,35 @@ module.exports = function(s,config,lang,app){
|
|||
'Connection': 'keep-alive',
|
||||
'Pragma': 'no-cache'
|
||||
});
|
||||
var contentWriter,content = fs.readFileSync(config.defaultMjpeg,'binary');
|
||||
res.write("--shinobi\r\n");
|
||||
res.write("Content-Type: image/jpeg\r\n");
|
||||
res.write("Content-Length: " + content.length + "\r\n");
|
||||
res.write("\r\n");
|
||||
res.write(content,'binary');
|
||||
res.write("\r\n");
|
||||
var ip = s.getClientIp(req)
|
||||
s.camera('watch_on',{
|
||||
id : req.params.id,
|
||||
ke : req.params.ke
|
||||
},{
|
||||
id : req.params.auth + ip + req.headers['user-agent']
|
||||
})
|
||||
Emitter.on('data',contentWriter=function(d){
|
||||
content = d;
|
||||
var contentWriter
|
||||
fs.readFile(config.defaultMjpeg,'binary',function(err,content){
|
||||
res.write("--shinobi\r\n");
|
||||
res.write("Content-Type: image/jpeg\r\n");
|
||||
res.write("Content-Length: " + content.length + "\r\n");
|
||||
res.write("\r\n");
|
||||
res.write(content,'binary');
|
||||
})
|
||||
res.on('close', function () {
|
||||
Emitter.removeListener('data',contentWriter)
|
||||
s.camera('watch_off',{
|
||||
res.write("\r\n");
|
||||
var ip = s.getClientIp(req)
|
||||
s.camera('watch_on',{
|
||||
id : req.params.id,
|
||||
ke : req.params.ke
|
||||
},{
|
||||
id : req.params.auth + ip + req.headers['user-agent']
|
||||
})
|
||||
});
|
||||
Emitter.on('data',contentWriter=function(d){
|
||||
content = d;
|
||||
res.write(content,'binary');
|
||||
})
|
||||
res.on('close', function () {
|
||||
Emitter.removeListener('data',contentWriter)
|
||||
s.camera('watch_off',{
|
||||
id : req.params.id,
|
||||
ke : req.params.ke
|
||||
},{
|
||||
id : req.params.auth + ip + req.headers['user-agent']
|
||||
})
|
||||
})
|
||||
})
|
||||
}else{
|
||||
res.end();
|
||||
}
|
||||
|
@ -212,20 +213,20 @@ module.exports = function(s,config,lang,app){
|
|||
s.checkChildProxy(req.params,function(){
|
||||
var Emitter,chunkChannel
|
||||
if(!req.params.channel){
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(req.params.channel)+config.pipeAddition
|
||||
}
|
||||
if(s.group[req.params.ke].mon[req.params.id].firstStreamChunk[chunkChannel]){
|
||||
if(s.group[req.params.ke].activeMonitors[req.params.id].firstStreamChunk[chunkChannel]){
|
||||
//variable name of contentWriter
|
||||
var contentWriter
|
||||
//set headers
|
||||
res.setHeader('Content-Type', 'video/x-flv');
|
||||
res.setHeader('Access-Control-Allow-Origin','*');
|
||||
//write first frame on stream
|
||||
res.write(s.group[req.params.ke].mon[req.params.id].firstStreamChunk[chunkChannel])
|
||||
res.write(s.group[req.params.ke].activeMonitors[req.params.id].firstStreamChunk[chunkChannel])
|
||||
var ip = s.getClientIp(req)
|
||||
s.camera('watch_on',{
|
||||
id : req.params.id,
|
||||
|
@ -262,10 +263,10 @@ module.exports = function(s,config,lang,app){
|
|||
s.checkChildProxy(req.params,function(){
|
||||
var Emitter,chunkChannel
|
||||
if(!req.params.channel){
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
|
||||
chunkChannel = 'MAIN'
|
||||
}else{
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
|
||||
chunkChannel = parseInt(req.params.channel)+config.pipeAddition
|
||||
}
|
||||
//variable name of contentWriter
|
||||
|
@ -312,9 +313,9 @@ module.exports = function(s,config,lang,app){
|
|||
if(!req.query.feed){req.query.feed='1'}
|
||||
var Emitter
|
||||
if(!req.params.feed){
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].streamIn[req.query.feed]
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.query.feed]
|
||||
}else{
|
||||
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.feed)+config.pipeAddition]
|
||||
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.feed)+config.pipeAddition]
|
||||
}
|
||||
var contentWriter
|
||||
var date = new Date();
|
||||
|
|
|
@ -634,4 +634,42 @@ module.exports = function(s,config,lang,app){
|
|||
res.end(s.prettyPrint(endData))
|
||||
},res,req)
|
||||
})
|
||||
/**
|
||||
* API : Superuser : Get Child Nodes
|
||||
*/
|
||||
app.all(config.webPaths.superApiPrefix+':auth/getChildNodes', function (req,res){
|
||||
s.superAuth(req.params,function(resp){
|
||||
var childNodesJson = {}
|
||||
Object.values(s.childNodes).forEach(function(activeNode){
|
||||
var activeCamerasCount = 0
|
||||
var activeCameras = {}
|
||||
Object.values(activeNode.activeCameras).forEach(function(monitor){
|
||||
++activeCamerasCount
|
||||
if(!activeCameras[monitor.ke])activeCameras[monitor.ke] = {}
|
||||
activeCameras[monitor.ke][monitor.mid] = {
|
||||
name: monitor.name,
|
||||
mid: monitor.mid,
|
||||
ke: monitor.ke,
|
||||
details: {
|
||||
accelerator: monitor.details.accelerator,
|
||||
auto_host: monitor.details.auto_host,
|
||||
}
|
||||
}
|
||||
})
|
||||
childNodesJson[activeNode.ip] = {
|
||||
ip: activeNode.ip,
|
||||
cpu: activeNode.cpu,
|
||||
dead: activeNode.dead,
|
||||
countCount: activeNode.countCount,
|
||||
activeMonitorsCount: activeCamerasCount,
|
||||
activeMonitors: activeCameras,
|
||||
}
|
||||
})
|
||||
var endData = {
|
||||
ok : true,
|
||||
childNodes: childNodesJson,
|
||||
}
|
||||
res.end(s.prettyPrint(endData))
|
||||
},res,req)
|
||||
})
|
||||
}
|
||||
|
|
3
sql/.gitignore
vendored
3
sql/.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
monitors.sql
|
||||
users.sql
|
||||
shinobi.sqlite
|
||||
users.sql
|
|
@ -13,7 +13,7 @@
|
|||
/*!40000 ALTER TABLE `Users` DISABLE KEYS */;
|
||||
INSERT INTO `Users` (`ke`, `uid`, `auth`, `mail`, `pass`, `details`) VALUES
|
||||
('2Df5hBE', 'XDf5hB3', 'ec49f05c1ddc7d818c61b3343c98cbc6', 'ccio@m03.ca', '5f4dcc3b5aa765d61d8327deb882cf99', '{"days":"10"}');
|
||||
INSERT INTO `Monitors` (`mid`, `ke`, `name`, `shto`, `shfr`, `details`, `type`, `ext`, `protocol`, `host`, `path`, `port`, `fps`, `mode`, `width`, `height`) VALUES ('Demo', '2Df5hBE', 'Demo', '[]', '[]', '{"fatal_max":"","notes":"","dir":"","rtsp_transport":"tcp","muser":"","mpass":"","port_force":"0","sfps":"","aduration":"1000000","probesize":"1000000","accelerator":"0","hwaccel":null,"hwaccel_vcodec":"","hwaccel_device":"","stream_type":"hls","stream_mjpeg_clients":"","stream_vcodec":"copy","stream_acodec":"no","hls_time":"","preset_stream":"","hls_list_size":"","signal_check":"","signal_check_log":null,"stream_quality":"","stream_fps":"1","stream_scale_x":"","stream_scale_y":"","rotate_stream":null,"svf":"","stream_timestamp":"0","stream_timestamp_font":"","stream_timestamp_font_size":"","stream_timestamp_color":"","stream_timestamp_box_color":"","stream_timestamp_x":"","stream_timestamp_y":"","stream_watermark":"0","stream_watermark_location":"","stream_watermark_position":null,"snap":"1","snap_fps":"","snap_scale_x":"","snap_scale_y":"","snap_vf":"","vcodec":"copy","crf":"","preset_record":"","acodec":"libvorbis","dqf":null,"cutoff":"10","rotate_record":null,"vf":"","timestamp":"1","timestamp_font":"","timestamp_font_size":"","timestamp_color":"","timestamp_box_color":"","timestamp_x":"","timestamp_y":"","watermark":null,"watermark_location":"","watermark_position":null,"cust_input":"","cust_snap":"","cust_detect":"","cust_stream":"","cust_stream_server":"","cust_record":"","custom_output":"","detector":"0","detector_webhook":null,"detector_webhook_url":"","detector_command_enable":null,"detector_command":"","detector_command_timeout":"","detector_lock_timeout":"","detector_save":null,"detector_frame_save":null,"detector_mail":null,"detector_mail_timeout":"","detector_record_method":null,"detector_trigger":null,"detector_trigger_record_fps":"","detector_timeout":"","watchdog_reset":null,"detector_delete_motionless_videos":null,"detector_send_frames":null,"detector_fps":"","detector_scale_x":"","detector_scale_y":"","detector_use_motion":null,"detector_use_detect_object":null,"detector_frame":null,"detector_sensitivity":"","cords":"","detector_lisence_plate":null,"detector_lisence_plate_country":null,"detector_notrigger":null,"detector_notrigger_mail":null,"detector_notrigger_timeout":"","control":"0","control_base_url":"","control_stop":null,"control_url_stop_timeout":"","control_url_center":"","control_url_left":"","control_url_left_stop":"","control_url_right":"","control_url_right_stop":"","control_url_up":"","control_url_up_stop":"","control_url_down":"","control_url_down_stop":"","control_url_enable_nv":"","control_url_disable_nv":"","control_url_zoom_out":"","control_url_zoom_out_stop":"","control_url_zoom_in":"","control_url_zoom_in_stop":"","groups":"","loglevel":"warning","sqllog":"0","detector_cascades":""}', 'mjpeg', 'mp4', 'http', 'came3.nkansai.ne.jp', '/nphMotionJpeg?Resolution=640x480&Quality=Motion', 81, 15, 'start', 640, 480);
|
||||
INSERT INTO `Monitors` (`mid`, `ke`, `name`, `shto`, `shfr`, `details`, `type`, `ext`, `protocol`, `host`, `path`, `port`, `fps`, `mode`, `width`, `height`) VALUES ('bunny', '2Df5hBE', 'Bunny', '[]', '[]', '{"fatal_max":"","notes":"","dir":"","rtsp_transport":"tcp","muser":"","mpass":"","port_force":"0","sfps":"","aduration":"1000000","probesize":"1000000","accelerator":"0","hwaccel":null,"hwaccel_vcodec":"","hwaccel_device":"","stream_type":"hls","stream_mjpeg_clients":"","stream_vcodec":"copy","stream_acodec":"no","hls_time":"","preset_stream":"","hls_list_size":"","signal_check":"","signal_check_log":null,"stream_quality":"","stream_fps":"1","stream_scale_x":"","stream_scale_y":"","rotate_stream":null,"svf":"","stream_timestamp":"0","stream_timestamp_font":"","stream_timestamp_font_size":"","stream_timestamp_color":"","stream_timestamp_box_color":"","stream_timestamp_x":"","stream_timestamp_y":"","stream_watermark":"0","stream_watermark_location":"","stream_watermark_position":null,"snap":"1","snap_fps":"","snap_scale_x":"","snap_scale_y":"","snap_vf":"","vcodec":"copy","crf":"","preset_record":"","acodec":"libvorbis","dqf":null,"cutoff":"10","rotate_record":null,"vf":"","timestamp":"1","timestamp_font":"","timestamp_font_size":"","timestamp_color":"","timestamp_box_color":"","timestamp_x":"","timestamp_y":"","watermark":null,"watermark_location":"","watermark_position":null,"cust_input":"","cust_snap":"","cust_detect":"","cust_stream":"","cust_stream_server":"","cust_record":"","custom_output":"","detector":"0","detector_webhook":null,"detector_webhook_url":"","detector_command_enable":null,"detector_command":"","detector_command_timeout":"","detector_lock_timeout":"","detector_save":null,"detector_frame_save":null,"detector_mail":null,"detector_mail_timeout":"","detector_record_method":null,"detector_trigger":null,"detector_trigger_record_fps":"","detector_timeout":"","watchdog_reset":null,"detector_delete_motionless_videos":null,"detector_send_frames":null,"detector_fps":"","detector_scale_x":"","detector_scale_y":"","detector_use_motion":null,"detector_use_detect_object":null,"detector_frame":null,"detector_sensitivity":"","cords":"","detector_lisence_plate":null,"detector_lisence_plate_country":null,"detector_notrigger":null,"detector_notrigger_mail":null,"detector_notrigger_timeout":"","control":"0","control_base_url":"","control_stop":null,"control_url_stop_timeout":"","control_url_center":"","control_url_left":"","control_url_left_stop":"","control_url_right":"","control_url_right_stop":"","control_url_up":"","control_url_up_stop":"","control_url_down":"","control_url_down_stop":"","control_url_enable_nv":"","control_url_disable_nv":"","control_url_zoom_out":"","control_url_zoom_out_stop":"","control_url_zoom_in":"","control_url_zoom_in_stop":"","groups":"","loglevel":"warning","sqllog":"0","detector_cascades":""}', 'mjpeg', 'mp4', 'http', 'came3.nkansai.ne.jp', '/nphMotionJpeg?Resolution=640x480&Quality=Motion', 81, 15, 'start', 640, 480);
|
||||
/*!40000 ALTER TABLE `Users` ENABLE KEYS */;
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
|
|
113
sql/docker/01-framework.sql
Normal file
113
sql/docker/01-framework.sql
Normal file
|
@ -0,0 +1,113 @@
|
|||
-- --------------------------------------------------------
|
||||
-- Host: 192.168.88.58
|
||||
-- Server version: 5.7.17-0ubuntu0.16.04.1 - (Ubuntu)
|
||||
-- Server OS: Linux
|
||||
-- HeidiSQL Version: 9.3.0.4984
|
||||
-- --------------------------------------------------------
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
|
||||
-- Dumping structure for table ccio.API
|
||||
CREATE TABLE IF NOT EXISTS `API` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`uid` varchar(50) DEFAULT NULL,
|
||||
`ip` tinytext,
|
||||
`code` varchar(100) DEFAULT NULL,
|
||||
`details` text,
|
||||
`time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Events
|
||||
CREATE TABLE IF NOT EXISTS `Events` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`mid` varchar(50) DEFAULT NULL,
|
||||
`details` text,
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Logs
|
||||
CREATE TABLE IF NOT EXISTS `Logs` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`mid` varchar(50) DEFAULT NULL,
|
||||
`info` text,
|
||||
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Monitors
|
||||
CREATE TABLE IF NOT EXISTS `Monitors` (
|
||||
`mid` varchar(50) DEFAULT NULL,
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`name` varchar(50) DEFAULT NULL,
|
||||
`shto` text,
|
||||
`shfr` text,
|
||||
`details` longtext,
|
||||
`type` varchar(50) DEFAULT 'jpeg',
|
||||
`ext` varchar(50) DEFAULT 'webm',
|
||||
`protocol` varchar(50) DEFAULT 'http',
|
||||
`host` varchar(100) DEFAULT '0.0.0.0',
|
||||
`path` varchar(100) DEFAULT '/',
|
||||
`port` int(8) DEFAULT '80',
|
||||
`fps` int(8) DEFAULT '1',
|
||||
`mode` varchar(15) DEFAULT NULL,
|
||||
`width` int(11) DEFAULT '640',
|
||||
`height` int(11) DEFAULT '360'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Presets
|
||||
CREATE TABLE IF NOT EXISTS `Presets` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`name` text,
|
||||
`details` text,
|
||||
`type` enum('monitor','event','user') DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Users
|
||||
CREATE TABLE IF NOT EXISTS `Users` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`uid` varchar(50) DEFAULT NULL,
|
||||
`auth` varchar(50) DEFAULT NULL,
|
||||
`mail` varchar(100) DEFAULT NULL,
|
||||
`pass` varchar(100) DEFAULT NULL,
|
||||
`details` longtext,
|
||||
UNIQUE KEY `mail` (`mail`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
|
||||
-- Dumping structure for table ccio.Videos
|
||||
CREATE TABLE IF NOT EXISTS `Videos` (
|
||||
`mid` varchar(50) DEFAULT NULL,
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`ext` enum('webm','mp4') DEFAULT NULL,
|
||||
`time` timestamp NULL DEFAULT NULL,
|
||||
`duration` float DEFAULT NULL,
|
||||
`size` float DEFAULT NULL,
|
||||
`frames` int(11) DEFAULT NULL,
|
||||
`end` timestamp NULL DEFAULT NULL,
|
||||
`status` int(1) DEFAULT '0' COMMENT '0:Building,1:Complete,2:Read,3:Archive',
|
||||
`details` text
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
|
@ -137,6 +137,27 @@ CREATE TABLE IF NOT EXISTS `Schedules` (
|
|||
`enabled` int(1) NOT NULL DEFAULT '1'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Dumping structure for table ccio.Timelapses
|
||||
CREATE TABLE IF NOT EXISTS `Timelapses` (
|
||||
`ke` varchar(50) NOT NULL,
|
||||
`mid` varchar(50) NOT NULL,
|
||||
`details` longtext,
|
||||
`date` date NOT NULL,
|
||||
`time` timestamp NOT NULL,
|
||||
`end` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`size` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Dumping structure for table ccio.Timelapse Frames
|
||||
CREATE TABLE IF NOT EXISTS `Timelapse Frames` (
|
||||
`ke` varchar(50) NOT NULL,
|
||||
`mid` varchar(50) NOT NULL,
|
||||
`details` longtext,
|
||||
`filename` varchar(50) NOT NULL,
|
||||
`time` timestamp NULL DEFAULT NULL,
|
||||
`size` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
|
|
|
@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS Monitors (
|
|||
name varchar(50) DEFAULT NULL,
|
||||
shto text,
|
||||
shfr text,
|
||||
details longtext,
|
||||
details text,
|
||||
type varchar(50) DEFAULT 'jpeg',
|
||||
ext varchar(50) DEFAULT 'webm',
|
||||
protocol varchar(50) DEFAULT 'http',
|
||||
|
@ -82,7 +82,7 @@ CREATE TABLE IF NOT EXISTS Users (
|
|||
auth varchar(50) DEFAULT NULL,
|
||||
mail varchar(100) DEFAULT NULL,
|
||||
pass varchar(100) DEFAULT NULL,
|
||||
details longtext,
|
||||
details text,
|
||||
CONSTRAINT mail UNIQUE (mail)
|
||||
) ;
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
CREATE USER 'majesticflame'@'127.0.0.1' IDENTIFIED BY '';
|
||||
GRANT ALL PRIVILEGES ON ccio.* TO 'majesticflame'@'127.0.0.1';
|
||||
FLUSH PRIVILEGES;
|
||||
FLUSH PRIVILEGES;
|
||||
|
|
3
web/libs/.gitignore
vendored
3
web/libs/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
demo
|
||||
demo
|
||||
impervious
|
||||
|
|
41
web/libs/css/bootstrap-slider.min.css
vendored
Normal file
41
web/libs/css/bootstrap-slider.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -460,4 +460,4 @@
|
|||
opacity: 1;
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,3 +87,361 @@ img{max-width:100%}
|
|||
.video_grid .thumb .title-strip{top:0;opacity:0.5}
|
||||
.video_grid .thumb .button-strip{bottom:0;opacity:0}
|
||||
.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1}
|
||||
|
||||
|
||||
|
||||
/* */
|
||||
.canvas_holder canvas{z-index:11}
|
||||
|
||||
.demo-blog .mdl-card__media ~ .mdl-card__supporting-text{position:relative;overflow:initial;cursor:move}
|
||||
.demo-blog .mdl-card__media ~ .mdl-card__supporting-text .btn-group{cursor: default}
|
||||
|
||||
.demo-blog .demo-blog__posts.montage{max-width:100%}
|
||||
|
||||
.hide-side .mdl-layout__drawer{overflow-y: hidden}
|
||||
.mdl-layout__header-row{padding-left:10!important}
|
||||
.mdl-layout__header-row .nav>li>a{border-radius:50%;}
|
||||
.mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff}
|
||||
|
||||
|
||||
.video_video{margin:auto;max-width:100%;max-height:600px;}
|
||||
#confirm_window .video_video{margin-top:15px}
|
||||
#confirm_window .info-table{margin-top:15px}
|
||||
@media (max-width: 768px){
|
||||
.full.modal .modal-body,.medium.modal .modal-body{max-height:400px;overflow:auto}
|
||||
}
|
||||
@media (min-width: 768px){
|
||||
.modal.full,.modal.medium{padding-left:0!important;}
|
||||
.modal.full .modal-dialog{width:calc(100% - 10px)!important;margin: 30px auto;max-width: none;}
|
||||
.modal.medium .modal-dialog{width:calc(70% - 10px)!important;margin: 30px auto;}
|
||||
.full.modal .modal-body,.medium.modal .modal-body{height:calc(100% - 200px);overflow:auto}
|
||||
}
|
||||
|
||||
.flex-container-modal-body{overflow: auto}
|
||||
.flex-container-modal-body .flex-block>div{flex:1;float:none}
|
||||
|
||||
.modal{overflow:auto!important}
|
||||
|
||||
.videos_list .title{font-size:12pt;padding:0 10px}
|
||||
|
||||
.demo-drawer{background:#2b2a2a;color:#fff;}
|
||||
.demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;}
|
||||
.demo-drawer::-webkit-scrollbar{display:none;}
|
||||
.small-square-img{height:40px;width:40px;border-radius:5px}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#side_menu_right.nav-xs{width:0!important;overflow:hidden}
|
||||
.side-menu table{color:#fff;}
|
||||
#main_canvas{background:#333;color:#fff;padding-top:0}
|
||||
#main_header{background:#222;color:#fff;}
|
||||
#logs_modal table tr td:first-child{width:10%}
|
||||
[class_toggle]{cursor:pointer}
|
||||
|
||||
.form-group label{width:100%}
|
||||
|
||||
.table-striped>tbody>tr>td{vertical-align:middle}
|
||||
.table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;}
|
||||
|
||||
|
||||
|
||||
|
||||
.follow-list ul{padding:0;margin:0;font-family:"Roboto","Helvetica","Arial",sans-serif;}
|
||||
.follow-list ul a:not(.btn){color:#fff}
|
||||
.os_bars{width:600px;display:inline-block;padding:5px 0 0 10px}
|
||||
@media screen and (max-width: 600px){
|
||||
.os_bars{width:200px;}
|
||||
.os_bars label{padding:2.5px 0;margin:0;font-size:8pt}
|
||||
}
|
||||
.os_bars .display-table .display-table-cell{padding:5px;vertical-align:center;width:33%}
|
||||
.progress{height:5px;margin:0;}
|
||||
.os_bars label,.os_bars .percent{padding:2.5px 0;margin:0;font-size:7.5pt}
|
||||
.ui-pnotify-hide .ui-pnotify{display:none!important}
|
||||
/*cool dropdown thing*/
|
||||
ul.msg_list li {
|
||||
background: #f7f7f7;color:#333;
|
||||
padding: 5px;
|
||||
display: list-item;
|
||||
margin: 6px 6px 0;
|
||||
width: 96% !important
|
||||
}
|
||||
|
||||
ul.msg_list li div{display:block}
|
||||
|
||||
ul.msg_list li:last-child {
|
||||
margin-bottom: 6px;
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
ul.msg_list li a {
|
||||
padding: 3px 5px !important
|
||||
}
|
||||
ul.msg_list li .progress {
|
||||
height:5px;margin:10px 0 0 0;
|
||||
}
|
||||
|
||||
ul.msg_list li .image img {
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
-webkit-border-radius: 2px 2px 2px 2px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
width: 11%
|
||||
}
|
||||
|
||||
ul.msg_list li .time {
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
ul.msg_list li .message {
|
||||
display: block !important;
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
.dropdown-menu.msg_list span {
|
||||
white-space: normal
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border: medium none;
|
||||
box-shadow: none;
|
||||
display: none;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
left: 0;
|
||||
list-style: none outside none;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
text-shadow: none;
|
||||
top: 100%;
|
||||
z-index: 9998;
|
||||
border: 1px solid #D9DEE4;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0
|
||||
}
|
||||
|
||||
.dropdown-menu>li>a {
|
||||
color: #5A738E
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
margin-top: 0;
|
||||
border: 1px solid #D9DEE4;
|
||||
-webkit-box-shadow: none;
|
||||
right: 0;
|
||||
left: auto;
|
||||
width: 220px
|
||||
}
|
||||
.is-small-screen .nav>li{display:inline-block}
|
||||
.navbar-nav .open .dropdown-menu li a{padding:7px 15px}
|
||||
.navbar-nav .open .dropdown-menu.msg_list {
|
||||
width: 300px
|
||||
}
|
||||
.nav>li>a{color:#fff}
|
||||
.nav>li>a:focus, .nav>li>a:hover,.nav .open>a, .nav .open>a:focus, .nav .open>a:hover{background:#867560}
|
||||
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen){
|
||||
|
||||
}
|
||||
@media screen and (min-width: 1025px){
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__drawer {
|
||||
width: 0px;transition:0.2s;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen) .mdl-layout__header .mdl-layout__drawer-button{
|
||||
display:none;
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__content{
|
||||
margin-left: 0px;transition:0.2s
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{
|
||||
margin-left: 0px;width:100%;transition:0.2s
|
||||
}
|
||||
.mdl-menu__item>div{display:flex;align-items: center;width:100%}
|
||||
.mdl-menu__item>div>i{margin-right:5px}
|
||||
|
||||
|
||||
/*Digital Zoom*/
|
||||
.stream-block{
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
.zoomGlass {
|
||||
overflow: hidden;
|
||||
transition: none;
|
||||
width: 175px; height: 175px;
|
||||
position: absolute;
|
||||
border-radius: 15px;
|
||||
border: 3px solid #ddd;
|
||||
z-index:9999;
|
||||
}
|
||||
.zoomGlass iframe,
|
||||
.zoomGlass video,
|
||||
.zoomGlass canvas{
|
||||
padding: 0!important;
|
||||
max-width: none!important;
|
||||
position: absolute;
|
||||
transition: none;
|
||||
}
|
||||
.zoomGlass .hoverShade{position:absolute;width:100%;height:100%}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*** custom checkboxes ***/
|
||||
|
||||
.checkbox input[type=checkbox] { display:none; } /* to hide the checkbox itself */
|
||||
.checkbox input[type=checkbox] + label:before {
|
||||
font-family: FontAwesome;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.checkbox input[type=checkbox] + label:before { content: "\f096"; } /* unchecked icon */
|
||||
.checkbox input[type=checkbox] + label:before { letter-spacing: 10px; } /* space between checkbox and label */
|
||||
|
||||
.checkbox input[type=checkbox]:checked + label:before { content: "\f046"; } /* checked icon */
|
||||
.checkbox input[type=checkbox]:checked + label:before { letter-spacing: 5px; } /* allow space for check mark */
|
||||
|
||||
/*Clock*/
|
||||
#time-date {font-size:12px; text-align:center;}
|
||||
@media screen and (min-width:1025px){
|
||||
#clock {padding-right:35px}
|
||||
}
|
||||
#clock ul { width:150px; margin:0 auto; padding:0px; list-style:none; text-align:center; }
|
||||
#clock ul li { display:inline; font-size:1.6em; text-align:center;font-family:monospace;}
|
||||
|
||||
#clock .point { position:relative; -moz-animation:mymove 1s ease infinite; -webkit-animation:mymove 1s ease infinite; }
|
||||
|
||||
/*custom vis.js css*/
|
||||
.vis-timeline{background:#212121;color:#fff;border-color:#444}
|
||||
.vis-time-axis .vis-text{color: #dedede}
|
||||
.vis-item.vis-range .vis-item-content{background:#333;color:#fff}
|
||||
.vis-time-axis .vis-grid.vis-minor{border-color:#444}
|
||||
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.full.modal .modal-body, .medium.modal .modal-body {
|
||||
height:70%
|
||||
}
|
||||
}
|
||||
|
||||
/*animations*/
|
||||
@keyframes blink {
|
||||
0% { opacity:1 }
|
||||
50% { opacity:0 }
|
||||
100% { opacity:1 }
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% { opacity:1 }
|
||||
50% { opacity:0 }
|
||||
100% { opacity:1 }
|
||||
}
|
||||
.blink,[mode="Record"] .lamp {
|
||||
-webkit-animation: blink 1s linear infinite;
|
||||
-moz-animation: blink 1s linear infinite;
|
||||
animation: blink 1s linear infinite;
|
||||
}
|
||||
|
||||
/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */
|
||||
.marc-toggle {
|
||||
width: 50px;
|
||||
height: 25px;
|
||||
}
|
||||
.marc-toggle.abs-bot-left {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
.marc-toggle.abs-bot-right {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
.marc-toggle input[type=checkbox]{
|
||||
height: 0;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.marc-toggle label {
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
background: grey;
|
||||
display: block;
|
||||
border-radius: 100px;
|
||||
margin-bottom: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.marc-toggle label:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #fff;
|
||||
border-radius: 90px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.marc-toggle input:checked + label {
|
||||
background: #00118c;
|
||||
}
|
||||
|
||||
.marc-toggle input:checked + label:after {
|
||||
left: calc(100% - 5px);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.marc-toggle label:active:after {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/*hexagon pattern*/
|
||||
.bg-hexagon {
|
||||
background-color: #054e9f;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23fdfdfd' fill-opacity='0.4' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.demo-drawer .mdl-menu__container{
|
||||
max-height: 500px!important;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
/* */
|
||||
.slider.slider-horizontal {
|
||||
width:100%!important
|
||||
}
|
||||
/* */
|
||||
.epic-text {
|
||||
margin:15px 0 0 0;
|
||||
text-align:center;
|
||||
color:#777;
|
||||
font-family: sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3pt;
|
||||
font-size: 8pt;
|
||||
}
|
||||
/* Start of custom table sorter */
|
||||
.table .table-header-sorter {
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table th.table-header-sorter i.fa-sort {
|
||||
color: #ccc;
|
||||
}
|
||||
.table th.table-header-sorter i {
|
||||
margin-left: 10px;
|
||||
}
|
||||
/* End of custom table sorter */
|
||||
|
|
45
web/libs/css/dash2.darktheme.css
Normal file
45
web/libs/css/dash2.darktheme.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
.dark .list-group-item{border-color: #444;background:#222}
|
||||
.dark .list-group-item.active{background:#c49a68;border-color:#a7865f}
|
||||
.novideos{text-transform: uppercase;text-align: center;border-bottom:0!important;padding-top: 55%!important;letter-spacing:2px}
|
||||
|
||||
.btn-warning {
|
||||
color: #fff;
|
||||
background-color: #c49a68;
|
||||
border-color: #c49a68;
|
||||
}
|
||||
.dark .table-striped>tbody>tr:nth-of-type(even){background:#616161}
|
||||
.dark .table-striped>tbody>tr>td{border-color:#222;color:#fff}
|
||||
.dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;}
|
||||
.dark .table-striped>tbody>tr:nth-of-type(odd){background-color: #4c4747;}
|
||||
.dark .table>tbody>tr.active>td{background:inherit;border:0}
|
||||
.dark code{color: #c49a68;background-color: #36333d;}
|
||||
.dark a:not(.btn){color: #c49a68;}
|
||||
|
||||
|
||||
|
||||
|
||||
.dark.form-control,.dark .form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #eee;
|
||||
background-color: #36333d;
|
||||
background-image: none;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.dark.form-control:focus,.dark .form-control:focus {
|
||||
color: #ddd;
|
||||
background-color: #333;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dark .slider-selection {
|
||||
background:#555;
|
||||
}
|
|
@ -22,23 +22,25 @@ form.modal-body{margin:0}
|
|||
.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;}
|
||||
.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;}
|
||||
.dark .form-group-group .mdl-list__item:hover{background:#555;}
|
||||
.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0}
|
||||
.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;}
|
||||
.form-group-group h4 small{color:#fff;}
|
||||
.form-group-group:visible:last-child,.form-group-group > .form-group:last-child{margin-bottom:0}
|
||||
.form-group-group > h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;}
|
||||
.form-group-group > h4:empty{padding:2px}
|
||||
.form-group-group > h4 small{color:#fff;}
|
||||
.form-group-group.red{border-color:#d9534f}
|
||||
.form-group-group.red h4{background:#d9534f;color:#fff}
|
||||
.form-group-group.red > h4{background:#d9534f;color:#fff}
|
||||
.form-group-group.purple{border-color:#3f51b5}
|
||||
.form-group-group.purple h4{background:#3f51b5;color:#fff}
|
||||
.form-group-group.purple > h4{background:#3f51b5;color:#fff}
|
||||
.form-group-group.blue{border-color:#337ab7}
|
||||
.form-group-group.blue h4{background:#337ab7;color:#fff}
|
||||
.form-group-group.blue > h4{background:#337ab7;color:#fff}
|
||||
.form-group-group.navy{border-color:#31708f}
|
||||
.form-group-group.navy h4{background:#31708f;color:#fff}
|
||||
.form-group-group.navy > h4{background:#31708f;color:#fff}
|
||||
.form-group-group.green{border-color:#449d44}
|
||||
.form-group-group.green h4{background:#449d44;color:#fff}
|
||||
.form-group-group.green > h4{background:#449d44;color:#fff}
|
||||
.form-group-group.forestgreen{border-color:#1e4046}
|
||||
.form-group-group.forestgreen h4{background:#1e4046;color:#fff}
|
||||
.form-group-group.forestgreen > h4{background:#1e4046;color:#fff}
|
||||
.form-group-group.orange{border-color:#c49a68}
|
||||
.form-group-group.orange h4{background:#c49a68;color:#fff}
|
||||
.form-group-group.orange > h4{background:#c49a68;color:#fff}
|
||||
.form-group-group.grey{border-color:#777}
|
||||
.form-group-group.grey h4{background:#777;color:#fff}
|
||||
.form-group-group.grey > h4{background:#777;color:#fff}
|
||||
.dark .form-group-group{background:#222}
|
||||
.form-group-group:last-child {margin-bottom: 0}
|
||||
|
|
17
web/libs/css/dash2.misc.css
Normal file
17
web/libs/css/dash2.misc.css
Normal file
|
@ -0,0 +1,17 @@
|
|||
#diskUsedList .title {
|
||||
letter-spacing: 2px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: 7pt;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
#diskUsedList .value {
|
||||
letter-spacing:2px;
|
||||
font-weight:100;
|
||||
}
|
||||
#diskUsedList .log-item {
|
||||
line-height: 1
|
||||
}
|
||||
#diskUsedList .log-item .progress {
|
||||
margin: 2px 0 0 0;
|
||||
}
|
|
@ -4,3 +4,4 @@
|
|||
.dark.modal .modal-body{background:#333;}
|
||||
.dark.modal .close{color:#fff;}
|
||||
.dark.modal{color:#fff;}
|
||||
#confirm_window {z-index: 11111}
|
||||
|
|
26
web/libs/css/dash2.monitorStates.css
Normal file
26
web/libs/css/dash2.monitorStates.css
Normal file
|
@ -0,0 +1,26 @@
|
|||
.state-monitor-row-fields{
|
||||
border-bottom: 1px solid #444;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.state-monitor-row-fields .list-group{
|
||||
height: 300px
|
||||
}
|
||||
.state-monitor-row .list-group .list-group-item:first-child{
|
||||
border-top:0;
|
||||
}
|
||||
.state-monitor-row .list-group .list-group-item:last-child{
|
||||
border-bottom:0;
|
||||
}
|
||||
|
||||
.state-monitor-row-select-container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.state-monitor-row-fields-container {
|
||||
border: 1px solid #444;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.state-monitor-row-fields-field {
|
||||
margin-top: 5px;
|
||||
}
|
|
@ -84,3 +84,20 @@ img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px}
|
|||
[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7}
|
||||
[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100}
|
||||
.signal.red{background:#c9302c}
|
||||
|
||||
|
||||
|
||||
#monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;}
|
||||
#monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none}
|
||||
|
||||
|
||||
#monitors_list .monitor_block{transition:none}
|
||||
.dropdown-menu.scrollable{max-height:300px}
|
||||
.upload_file input{display:none}
|
||||
#video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto}
|
||||
.stream-block,.stream-objects{overflow: hidden!important}
|
||||
.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}
|
||||
.stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;}
|
||||
.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px}
|
||||
.stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px}
|
||||
.stream-objects .point{position:absolute;top:0;left:0;border:3px solid red;border-radius:50%}
|
||||
|
|
176
web/libs/css/dash2.powerVideo2.css
Normal file
176
web/libs/css/dash2.powerVideo2.css
Normal file
|
@ -0,0 +1,176 @@
|
|||
#powerVideo .videoPlayer {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
#powerVideo .videoPlayer video{
|
||||
max-width: 100%;
|
||||
height: 300px;
|
||||
object-fit: fill;
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer:fullscreen video{
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#powerVideoMonitorControls{
|
||||
border-radius: 0 0 5px 5px;
|
||||
padding: 5px;
|
||||
background: #222;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#powerVideoMonitorsList{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#powerVideoMonitorsList .list-item{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#powerVideoMonitorViews {
|
||||
text-align: center;
|
||||
min-height: 300px;
|
||||
background: #444;
|
||||
border-radius: 5px 5px 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer .videoPlayer-detection-info {
|
||||
position: absolute;
|
||||
padding: 20px 10px 20px 10px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
z-index: 11;
|
||||
opacity: 0;
|
||||
background: rgba(0,0,0,0.7);
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
overflow: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer:hover .videoPlayer-detection-info,
|
||||
#powerVideo .videoPlayer.show-detection-info .videoPlayer-detection-info {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer .videoPlayer-stream-objects {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer .videoPlayer-detection-info-object div {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#powerVideo .videoPlayer .videoPlayer-detection-info-object {
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.videoPlayer-stream-objects .tag {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
background: red;
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
font-size: 80%;
|
||||
border-radius: 5px 5px 0 0;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.videoPlayer-stream-objects .stream-detected-object {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: 3px solid red;
|
||||
background: transparent;
|
||||
border-radius: 5px
|
||||
}
|
||||
|
||||
.videoPlayer-stream-objects .stream-detected-point {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: 3px solid yellow;
|
||||
background: transparent;
|
||||
border-radius: 5px
|
||||
}
|
||||
|
||||
.videoPlayer-stream-objects .point {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: 3px solid red;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
/* loading */
|
||||
#powerVideo .loading {
|
||||
font-size: 20pt;
|
||||
text-align: center;
|
||||
}
|
||||
#powerVideo .loading > div {
|
||||
margin-top: 5px
|
||||
}
|
||||
|
||||
/* VIS.js */
|
||||
|
||||
#powerVideo video {
|
||||
width: 100%;
|
||||
padding: 6px 0 0 0
|
||||
}
|
||||
|
||||
#powerVideo .videoAfter,
|
||||
#powerVideo .videoBefore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#powerVideo .vis-timeline {
|
||||
font-family: monospace;
|
||||
border-radius: 5px;
|
||||
border-color: #172b4d;
|
||||
}
|
||||
#powerVideo .vis-item {
|
||||
border-color: #347af1;
|
||||
background-color: #4d87d0;
|
||||
color: #fff;
|
||||
}
|
||||
#powerVideo .vis-item.vis-selected {
|
||||
border-color: #f5365c;
|
||||
background-color: #f5365c;
|
||||
color: #fff;
|
||||
}
|
||||
#powerVideo .vis-panel.vis-bottom {
|
||||
border: 1px #17294b;
|
||||
}
|
||||
#powerVideo .vis-time-axis .vis-grid.vis-minor{
|
||||
border-color: #1a539a;
|
||||
}
|
||||
#powerVideo .vis-time-axis .vis-grid.vis-major {
|
||||
border-color: #4d87d0;
|
||||
}
|
||||
#powerVideo .vis-time-axis .vis-text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
[timeline-video-file] .progress{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
opacity: 0;
|
||||
}
|
||||
.vis-selected [timeline-video-file] .progress{
|
||||
opacity: 1;
|
||||
}
|
|
@ -6,3 +6,13 @@
|
|||
#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4}
|
||||
#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent}
|
||||
#pvideo_viewer h3{font-family:monospace}
|
||||
|
||||
|
||||
#vis_pwrvideo{height:250px}
|
||||
|
||||
|
||||
#vis_monitors{overflow:auto;max-height:400px}
|
||||
#vis_monitors .btn-group-vertical{width:100%}
|
||||
|
||||
|
||||
#motion_list{height:155px;overflow:auto;border-radius:5px;border:1px solid #444;position:relative;background: #222;margin:0}
|
||||
|
|
|
@ -3,3 +3,7 @@
|
|||
#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;}
|
||||
#region_editor .cord_element.selected{z-index:12;}
|
||||
#region_editor .cord_element .controls{margin-bottom:5px;}
|
||||
|
||||
|
||||
|
||||
#region_editor_live iframe,.canvas_holder canvas{border:0;position:absolute;left:0;top:0}
|
||||
|
|
34
web/libs/css/dash2.sidemenu.css
Normal file
34
web/libs/css/dash2.sidemenu.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
#accbtn{
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.side-menu .monitor_block{padding:0;position:relative}
|
||||
.side-menu .monitor_block img{width:100%;height:75px;cursor:pointer;border: 0.5px inset #263238;}
|
||||
@media screen and (max-width:1025px){
|
||||
.side-menu .monitor_block img{height:175px;}
|
||||
}
|
||||
.side-menu .monitor_block:hover .icons{opacity:1}
|
||||
.side-menu .monitor_block:hover .title{opacity:1}
|
||||
.side-menu .monitor_block .icons,.side-menu .monitor_block .title{opacity:0;width:100%;bottom:0;left:0;background:rgba(0,0,0,0.6);position:absolute;padding:2.5px;z-index:11;cursor:move}
|
||||
.side-menu .monitor_block .title{bottom:auto;top:0;color:#fff}
|
||||
.nav-xs.side-menu .monitor_block{width:100%}
|
||||
.side-menu .monitor_block .list-data{display:none}
|
||||
.output_data:empty{display:none}
|
||||
.output_data{max-height:500px;font-family:monospace;padding:10px;border-radius:5px;background:#f3f3f3;overflow:auto}
|
||||
.dark .output_data{background:#222;}
|
||||
#probe .output_data div>div{margin-left:10px}
|
||||
.side-menu.list-blocks .monitor_block .icons,.side-menu.list-blocks .monitor_block .title{position:inherit;opacity:1;background:none}
|
||||
.side-menu.list-blocks .monitor_block .title{padding:5px;border-radius:5px;background:#222;}
|
||||
.side-menu.list-blocks .monitor_block:not(:last-child){border-bottom: 1px solid #54502d;}
|
||||
.side-menu.list-blocks .monitor_block:first-child{border-top: 1px solid #54502d;}
|
||||
.side-menu.list-blocks .monitor_block{float:none;width:100%;padding: 10px}
|
||||
.side-menu.list-blocks .monitor_block.ui-sortable-helper{background:rgba(0,0,0,0.6);border-radius:5px;padding:5px;border:0}
|
||||
.side-menu.list-blocks .monitor_block .list-data{display:block}
|
||||
.side-menu.list-blocks .monitor_block img{width:60px;height:60px;cursor:pointer;display:inline-block;margin-right:10px;border-radius:50%;vertical-align:top;border:0}
|
||||
.nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;}
|
||||
.side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block}
|
||||
.nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none}
|
||||
.side-menu .mdl-menu{z-index: 12}
|
110
web/libs/css/dash2.timelapse.jpeg.css
Normal file
110
web/libs/css/dash2.timelapse.jpeg.css
Normal file
|
@ -0,0 +1,110 @@
|
|||
#timelapsejpeg .frames .frame{
|
||||
height: 100px;
|
||||
}
|
||||
#timelapsejpeg .frames .frame img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#timelapsejpeg .frameStrip img{
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
top:0;
|
||||
left:0;
|
||||
}
|
||||
#timelapsejpeg .modal-body > .row,
|
||||
#timelapsejpeg .modal-body > .row > div {
|
||||
position:relative;
|
||||
height: 100%;
|
||||
}
|
||||
#timelapsejpeg .frameIcons .frame{
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#timelapsejpeg .frameIcons .frame.selected{
|
||||
border: 3px solid #ffc96b;
|
||||
}
|
||||
#timelapsejpeg .frameIcons > div:not(:last-child){
|
||||
margin-bottom: 15px
|
||||
}
|
||||
#timelapsejpeg .frameIcons{
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#timelapsejpeg .frameIcons .frame{
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
#timelapsejpeg .frameIcons .frame .shade{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
background: rgba(0,0,0,0.6);
|
||||
color: #fff
|
||||
}
|
||||
#timelapsejpeg .playBackView{
|
||||
position:absolute;
|
||||
height: 50%;
|
||||
width:100%;
|
||||
top:0;
|
||||
left:0;
|
||||
background:#333;
|
||||
}
|
||||
#timelapsejpeg .playBackView img{
|
||||
height:100%;
|
||||
}
|
||||
#timelapsejpeg .liveStreamView{
|
||||
position:absolute;
|
||||
text-align: center;
|
||||
height: 50%;
|
||||
width:100%;
|
||||
top:50%;
|
||||
left:0;
|
||||
background:#333;
|
||||
}
|
||||
#timelapsejpeg .liveStreamView iframe{
|
||||
width:100%;
|
||||
height:100%;
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
border: 0;
|
||||
}
|
||||
/* stand alone */
|
||||
#timelapsejpeg.standalone {
|
||||
background: #2f2f2f;
|
||||
color:#fff;
|
||||
}
|
||||
#timelapsejpeg.standalone .fieldHolder{
|
||||
padding-top: 20px;
|
||||
}
|
||||
.scroll-style-6::-webkit-scrollbar-track
|
||||
{
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #333;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.scroll-style-6::-webkit-scrollbar
|
||||
{
|
||||
width: 10px;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.scroll-style-6::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #555;
|
||||
background-image: -webkit-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, .2) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, .2) 50%,
|
||||
rgba(255, 255, 255, .2) 75%,
|
||||
transparent 75%,
|
||||
transparent)
|
||||
}
|
|
@ -1,400 +0,0 @@
|
|||
#accbtn{
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#vis_pwrvideo{height:250px}
|
||||
#monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;}
|
||||
#monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none}
|
||||
#region_editor_live iframe,.canvas_holder canvas{border:0;position:absolute;left:0;top:0}
|
||||
.canvas_holder canvas{z-index:11}
|
||||
|
||||
.demo-blog .mdl-card__media ~ .mdl-card__supporting-text{position:relative;overflow:initial;cursor:move}
|
||||
.demo-blog .mdl-card__media ~ .mdl-card__supporting-text .btn-group{cursor: default}
|
||||
|
||||
.demo-blog .demo-blog__posts.montage{max-width:100%}
|
||||
|
||||
.mdl-layout__drawer{overflow-y: visible!important}
|
||||
.hide-side .mdl-layout__drawer{overflow-y: hidden}
|
||||
.mdl-layout__header-row{padding-left:10!important}
|
||||
.mdl-layout__header-row .nav>li>a{border-radius:50%;}
|
||||
.mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff}
|
||||
|
||||
|
||||
.video_video{margin:auto;max-width:100%;max-height:600px;}
|
||||
#confirm_window .video_video{margin-top:15px}
|
||||
#confirm_window .info-table{margin-top:15px}
|
||||
@media (max-width: 768px){
|
||||
.full.modal .modal-body,.medium.modal .modal-body{max-height:400px;overflow:auto}
|
||||
}
|
||||
@media (min-width: 768px){
|
||||
.modal.full,.modal.medium{padding-left:0!important;}
|
||||
.modal.full .modal-dialog{width:calc(100% - 10px)!important;margin: 30px auto;}
|
||||
.modal.medium .modal-dialog{width:calc(70% - 10px)!important;margin: 30px auto;}
|
||||
.full.modal .modal-body,.medium.modal .modal-body{height:calc(100% - 200px);overflow:auto}
|
||||
}
|
||||
|
||||
.flex-container-modal-body{overflow: auto}
|
||||
.flex-container-modal-body .flex-block>div{flex:1;float:none}
|
||||
|
||||
.modal{overflow:auto!important}
|
||||
|
||||
.videos_list .title{font-size:12pt;padding:0 10px}
|
||||
|
||||
.demo-drawer{background:#2b2a2a;color:#fff;}
|
||||
.demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;}
|
||||
.demo-drawer::-webkit-scrollbar{display:none;}
|
||||
.small-square-img{height:40px;width:40px;border-radius:5px}
|
||||
|
||||
.side-menu .monitor_block{padding:0;position:relative}
|
||||
.side-menu .monitor_block img{width:100%;height:75px;cursor:pointer;border: 0.5px inset #263238;}
|
||||
@media screen and (max-width:1025px){
|
||||
.side-menu .monitor_block img{height:175px;}
|
||||
}
|
||||
.side-menu .monitor_block:hover .icons{opacity:1}
|
||||
.side-menu .monitor_block:hover .title{opacity:1}
|
||||
.side-menu .monitor_block .icons,.side-menu .monitor_block .title{opacity:0;width:100%;bottom:0;left:0;background:rgba(0,0,0,0.6);position:absolute;padding:2.5px;z-index:11;cursor:move}
|
||||
.side-menu .monitor_block .title{bottom:auto;top:0;color:#fff}
|
||||
.nav-xs.side-menu .monitor_block{width:100%}
|
||||
.side-menu .monitor_block .list-data{display:none}
|
||||
.output_data:empty{display:none}
|
||||
.output_data{max-height:500px;font-family:monospace;padding:10px;border-radius:5px;background:#f3f3f3;overflow:auto}
|
||||
.dark .output_data{background:#222;}
|
||||
#probe .output_data div>div{margin-left:10px}
|
||||
.side-menu.list-blocks .monitor_block .icons,.side-menu.list-blocks .monitor_block .title{position:inherit;opacity:1;background:none}
|
||||
.side-menu.list-blocks .monitor_block .title{padding:5px;border-radius:5px;background:#222;}
|
||||
.side-menu.list-blocks .monitor_block:not(:last-child){border-bottom: 1px solid #54502d;}
|
||||
.side-menu.list-blocks .monitor_block:first-child{border-top: 1px solid #54502d;}
|
||||
.side-menu.list-blocks .monitor_block{float:none;width:100%;padding: 10px}
|
||||
.side-menu.list-blocks .monitor_block.ui-sortable-helper{background:rgba(0,0,0,0.6);border-radius:5px;padding:5px;border:0}
|
||||
.side-menu.list-blocks .monitor_block .list-data{display:block}
|
||||
.side-menu.list-blocks .monitor_block img{width:60px;height:60px;cursor:pointer;display:inline-block;margin-right:10px;border-radius:50%;vertical-align:top;border:0}
|
||||
.nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;}
|
||||
.side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block}
|
||||
.nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none}
|
||||
.side-menu .mdl-menu{z-index: 12}
|
||||
|
||||
#monitors_list .monitor_block{transition:none}
|
||||
.dropdown-menu.scrollable{max-height:300px}
|
||||
.upload_file input{display:none}
|
||||
#video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto}
|
||||
.stream-block,.stream-objects{overflow: hidden!important}
|
||||
.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}
|
||||
.stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;}
|
||||
.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px}
|
||||
.stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px}
|
||||
.stream-objects .point{position:absolute;top:0;left:0;border:3px solid red;border-radius:50%}
|
||||
|
||||
|
||||
#side_menu_right.nav-xs{width:0!important;overflow:hidden}
|
||||
.side-menu table{color:#fff;}
|
||||
#main_canvas{background:#333;color:#fff;padding-top:0}
|
||||
#main_header{background:#222;color:#fff;}
|
||||
#logs_modal table tr td:first-child{width:10%}
|
||||
[class_toggle]{cursor:pointer}
|
||||
|
||||
.form-group label{width:100%}
|
||||
|
||||
|
||||
|
||||
#vis_monitors{overflow:auto;max-height:400px}
|
||||
#vis_monitors .btn-group-vertical{width:100%}
|
||||
|
||||
|
||||
.table-striped>tbody>tr>td{vertical-align:middle}
|
||||
.table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;}
|
||||
#motion_list{height:155px;overflow:auto;border-radius:5px;border:1px solid #444;position:relative;background: #222;margin:0}
|
||||
.dark .list-group-item{border-color: #444;background:#222}
|
||||
.dark .list-group-item.active{background:#c49a68;border-color:#a7865f}
|
||||
.novideos{text-transform: uppercase;text-align: center;border-bottom:0!important;padding-top: 55%!important;letter-spacing:2px}
|
||||
|
||||
.btn-warning {
|
||||
color: #fff;
|
||||
background-color: #c49a68;
|
||||
border-color: #c49a68;
|
||||
}
|
||||
.dark .table-striped>tbody>tr:nth-of-type(even){background:#616161}
|
||||
.dark .table-striped>tbody>tr>td{border-color:#222;color:#fff}
|
||||
.dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;}
|
||||
.dark .table-striped>tbody>tr:nth-of-type(odd){background-color: #4c4747;}
|
||||
.dark .table>tbody>tr.active>td{background:inherit;border:0}
|
||||
.dark code{color: #c49a68;background-color: #36333d;}
|
||||
.dark a:not(.btn){color: #c49a68;}
|
||||
.follow-list ul{padding:0;margin:0;font-family:"Roboto","Helvetica","Arial",sans-serif;}
|
||||
.follow-list ul a:not(.btn){color:#fff}
|
||||
.os_bars{width:600px;display:inline-block;padding:5px 0 0 10px}
|
||||
@media screen and (max-width: 600px){
|
||||
.os_bars{width:200px;}
|
||||
.os_bars label{padding:2.5px 0;margin:0;font-size:8pt}
|
||||
}
|
||||
.os_bars .display-table .display-table-cell{padding:5px;vertical-align:center;width:33%}
|
||||
.progress{height:5px;margin:0;}
|
||||
.os_bars label,.os_bars .percent{padding:2.5px 0;margin:0;font-size:7.5pt}
|
||||
.ui-pnotify-hide .ui-pnotify{display:none!important}
|
||||
/*cool dropdown thing*/
|
||||
ul.msg_list li {
|
||||
background: #f7f7f7;color:#333;
|
||||
padding: 5px;
|
||||
display: list-item;
|
||||
margin: 6px 6px 0;
|
||||
width: 96% !important
|
||||
}
|
||||
|
||||
ul.msg_list li div{display:block}
|
||||
|
||||
ul.msg_list li:last-child {
|
||||
margin-bottom: 6px;
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
ul.msg_list li a {
|
||||
padding: 3px 5px !important
|
||||
}
|
||||
ul.msg_list li .progress {
|
||||
height:5px;margin:10px 0 0 0;
|
||||
}
|
||||
|
||||
ul.msg_list li .image img {
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
-webkit-border-radius: 2px 2px 2px 2px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
width: 11%
|
||||
}
|
||||
|
||||
ul.msg_list li .time {
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
ul.msg_list li .message {
|
||||
display: block !important;
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
.dropdown-menu.msg_list span {
|
||||
white-space: normal
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border: medium none;
|
||||
box-shadow: none;
|
||||
display: none;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
left: 0;
|
||||
list-style: none outside none;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
text-shadow: none;
|
||||
top: 100%;
|
||||
z-index: 9998;
|
||||
border: 1px solid #D9DEE4;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0
|
||||
}
|
||||
|
||||
.dropdown-menu>li>a {
|
||||
color: #5A738E
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
margin-top: 0;
|
||||
border: 1px solid #D9DEE4;
|
||||
-webkit-box-shadow: none;
|
||||
right: 0;
|
||||
left: auto;
|
||||
width: 220px
|
||||
}
|
||||
.is-small-screen .nav>li{display:inline-block}
|
||||
.navbar-nav .open .dropdown-menu li a{padding:7px 15px}
|
||||
.navbar-nav .open .dropdown-menu.msg_list {
|
||||
width: 300px
|
||||
}
|
||||
.nav>li>a{color:#fff}
|
||||
.nav>li>a:focus, .nav>li>a:hover,.nav .open>a, .nav .open>a:focus, .nav .open>a:hover{background:#867560}
|
||||
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen){
|
||||
|
||||
}
|
||||
@media screen and (min-width: 1025px){
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__drawer {
|
||||
width: 0px;transition:0.2s
|
||||
}
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen) .mdl-layout__header .mdl-layout__drawer-button{
|
||||
display:none;
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__content{
|
||||
margin-left: 0px;transition:0.2s
|
||||
}
|
||||
.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{
|
||||
margin-left: 0px;width:100%;transition:0.2s
|
||||
}
|
||||
.mdl-menu__item>div{display:flex;align-items: center;width:100%}
|
||||
.mdl-menu__item>div>i{margin-right:5px}
|
||||
|
||||
|
||||
/*Digital Zoom*/
|
||||
.stream-block{
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
.zoomGlass {
|
||||
overflow: hidden;
|
||||
transition: none;
|
||||
width: 175px; height: 175px;
|
||||
position: absolute;
|
||||
border-radius: 15px;
|
||||
border: 3px solid #ddd;
|
||||
z-index:9999;
|
||||
}
|
||||
.zoomGlass iframe,.zoomGlass canvas{position:absolute;transition: none;}
|
||||
.zoomGlass .hoverShade{position:absolute;width:100%;height:100%}
|
||||
|
||||
.dark.form-control,.dark .form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #eee;
|
||||
background-color: #36333d;
|
||||
background-image: none;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.dark.form-control:focus,.dark .form-control:focus {
|
||||
color: #ddd;
|
||||
background-color: #333;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
/*** custom checkboxes ***/
|
||||
|
||||
.checkbox input[type=checkbox] { display:none; } /* to hide the checkbox itself */
|
||||
.checkbox input[type=checkbox] + label:before {
|
||||
font-family: FontAwesome;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.checkbox input[type=checkbox] + label:before { content: "\f096"; } /* unchecked icon */
|
||||
.checkbox input[type=checkbox] + label:before { letter-spacing: 10px; } /* space between checkbox and label */
|
||||
|
||||
.checkbox input[type=checkbox]:checked + label:before { content: "\f046"; } /* checked icon */
|
||||
.checkbox input[type=checkbox]:checked + label:before { letter-spacing: 5px; } /* allow space for check mark */
|
||||
|
||||
/*Clock*/
|
||||
#time-date {font-size:12px; text-align:center;}
|
||||
@media screen and (min-width:1025px){
|
||||
#clock {padding-right:35px}
|
||||
}
|
||||
#clock ul { width:150px; margin:0 auto; padding:0px; list-style:none; text-align:center; }
|
||||
#clock ul li { display:inline; font-size:1.6em; text-align:center;font-family:monospace;}
|
||||
|
||||
#clock .point { position:relative; -moz-animation:mymove 1s ease infinite; -webkit-animation:mymove 1s ease infinite; }
|
||||
|
||||
/*custom vis.js css*/
|
||||
.vis-timeline{background:#212121;color:#fff;border-color:#444}
|
||||
.vis-time-axis .vis-text{color: #dedede}
|
||||
.vis-item.vis-range .vis-item-content{background:#333;color:#fff}
|
||||
.vis-time-axis .vis-grid.vis-minor{border-color:#444}
|
||||
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.full.modal .modal-body, .medium.modal .modal-body {
|
||||
height:70%
|
||||
}
|
||||
}
|
||||
|
||||
/*animations*/
|
||||
@keyframes blink {
|
||||
0% { opacity:1 }
|
||||
50% { opacity:0 }
|
||||
100% { opacity:1 }
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% { opacity:1 }
|
||||
50% { opacity:0 }
|
||||
100% { opacity:1 }
|
||||
}
|
||||
.blink,[mode="Record"] .lamp {
|
||||
-webkit-animation: blink 1s linear infinite;
|
||||
-moz-animation: blink 1s linear infinite;
|
||||
animation: blink 1s linear infinite;
|
||||
}
|
||||
|
||||
/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */
|
||||
.marc-toggle {
|
||||
width: 50px;
|
||||
height: 25px;
|
||||
}
|
||||
.marc-toggle.abs-bot-left {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
.marc-toggle.abs-bot-right {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
.marc-toggle input[type=checkbox]{
|
||||
height: 0;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.marc-toggle label {
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
background: grey;
|
||||
display: block;
|
||||
border-radius: 100px;
|
||||
margin-bottom: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.marc-toggle label:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #fff;
|
||||
border-radius: 90px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.marc-toggle input:checked + label {
|
||||
background: #00118c;
|
||||
}
|
||||
|
||||
.marc-toggle input:checked + label:after {
|
||||
left: calc(100% - 5px);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.marc-toggle label:active:after {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/*hexagon pattern*/
|
||||
.bg-hexagon {
|
||||
background-color: #054e9f;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23fdfdfd' fill-opacity='0.4' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
1
web/libs/css/vis.min.css
vendored
Normal file
1
web/libs/css/vis.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 223 KiB |
Binary file not shown.
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 218 KiB |
5
web/libs/js/bootstrap-slider.min.js
vendored
Normal file
5
web/libs/js/bootstrap-slider.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
$(document).ready(function() {
|
||||
var monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
|
||||
var dayNames= ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
|
||||
var monthNames = [ lang.January, lang.February, lang.March, lang.April, lang.May, lang.June, lang.July, lang.August, lang.September, lang.October, lang.November, lang.December ];
|
||||
var dayNames= [lang.Sunday, lang.Monday, lang.Tuesday, lang.Wednesday, lang.Thursday, lang.Friday, lang.Saturday]
|
||||
|
||||
var newDate = new Date();
|
||||
newDate.setDate(newDate.getDate());
|
||||
|
|
|
@ -12,6 +12,16 @@ $.ccio.permissionCheck = function(toCheck,monitorId){
|
|||
}
|
||||
return false
|
||||
}
|
||||
$.parseJSON = function(string){
|
||||
var parsed
|
||||
try{
|
||||
parsed = JSON.parse(string)
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
if(!parsed)parsed = string
|
||||
return parsed
|
||||
}
|
||||
$.ccio.op = function(r,rr,rrr){
|
||||
if(!rrr){rrr={};};if(typeof rrr === 'string'){rrr={n:rrr}};if(!rrr.n){rrr.n='ShinobiOptions_'+location.host}
|
||||
ii={o:localStorage.getItem(rrr.n)};try{ii.o=JSON.parse(ii.o)}catch(e){ii.o={}}
|
||||
|
@ -113,25 +123,27 @@ $.ccio.base64ArrayBuffer = function(arrayBuffer) {
|
|||
|
||||
return base64
|
||||
}
|
||||
$.ccio.snapshot=function(e,cb){
|
||||
var image_data,url;
|
||||
e.details=JSON.parse(e.mon.details);
|
||||
$.ccio.snapshot = function(options,cb){
|
||||
var image_data
|
||||
var url
|
||||
var monitor = options.mon || options.monitor || options
|
||||
var details = $.parseJSON(monitor.details)
|
||||
if($.ccio.op().jpeg_on!==true){
|
||||
var extend=function(image_data,width,height){
|
||||
var len = image_data.length
|
||||
var arraybuffer = new Uint8Array( len );
|
||||
var arraybuffer = new Uint8Array( len )
|
||||
for (var i = 0; i < len; i++) {
|
||||
arraybuffer[i] = image_data.charCodeAt(i);
|
||||
arraybuffer[i] = image_data.charCodeAt(i)
|
||||
}
|
||||
try {
|
||||
var blob = new Blob([arraybuffer], {type: 'application/octet-stream'});
|
||||
var blob = new Blob([arraybuffer], {type: 'application/octet-stream'})
|
||||
} catch (e) {
|
||||
var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder);
|
||||
var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder)
|
||||
bb.append(arraybuffer);
|
||||
var blob = bb.getBlob('application/octet-stream');
|
||||
}
|
||||
url = (window.URL || window.webkitURL).createObjectURL(blob);
|
||||
finish(url,image_data,width,height);
|
||||
url = (window.URL || window.webkitURL).createObjectURL(blob)
|
||||
finish(url,image_data,width,height)
|
||||
try{
|
||||
setTimeout(function(){
|
||||
URL.revokeObjectURL(url)
|
||||
|
@ -139,142 +151,191 @@ $.ccio.snapshot=function(e,cb){
|
|||
}catch(er){}
|
||||
}
|
||||
var finish = function(url,image_data,width,height){
|
||||
cb(url,image_data,width,height);
|
||||
cb(url,image_data,width,height)
|
||||
}
|
||||
switch(JSON.parse(e.mon.details).stream_type){
|
||||
switch(details.stream_type){
|
||||
case'hls':case'flv':case'mp4':
|
||||
$.ccio.snapshotVideo($('[mid='+e.mon.mid+'].monitor_item video')[0],function(base64,video_data,width,height){
|
||||
$.ccio.snapshotVideo($('[mid='+monitor.mid+'][ke='+monitor.ke+'][auth='+monitor.user.auth_token+'].monitor_item video')[0],function(base64,video_data,width,height){
|
||||
extend(video_data,width,height)
|
||||
})
|
||||
break;
|
||||
case'mjpeg':
|
||||
$('#temp').html('<canvas></canvas>')
|
||||
var c = $('#temp canvas')[0];
|
||||
var img = $('img',$('[mid='+e.mon.mid+'].monitor_item .stream-element').contents())[0];
|
||||
c.width = img.width;
|
||||
c.height = img.height;
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0,c.width,c.height);
|
||||
var c = $('#temp canvas')[0]
|
||||
var img = $('img',$('[mid='+monitor.mid+'][ke='+monitor.ke+'][auth='+monitor.user.auth_token+'].monitor_item .stream-element').contents())[0]
|
||||
c.width = img.width
|
||||
c.height = img.height
|
||||
var ctx = c.getContext('2d')
|
||||
ctx.drawImage(img, 0, 0,c.width,c.height)
|
||||
extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height)
|
||||
break;
|
||||
case'h265':
|
||||
var c = $('[mid='+e.mon.mid+'].monitor_item canvas')[0];
|
||||
var ctx = c.getContext('2d');
|
||||
var c = $('[mid='+monitor.mid+'][ke='+monitor.ke+'][auth='+monitor.user.auth_token+'].monitor_item canvas')[0]
|
||||
var ctx = c.getContext('2d')
|
||||
extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height)
|
||||
break;
|
||||
case'b64':
|
||||
base64 = e.mon.last_frame.split(',')[1];
|
||||
var image_data = new Image();
|
||||
image_data.src = base64;
|
||||
base64 = monitor.last_frame.split(',')[1]
|
||||
var image_data = new Image()
|
||||
image_data.src = base64
|
||||
extend(atob(base64),image_data.width,image_data.height)
|
||||
break;
|
||||
case'jpeg':case'h265':
|
||||
url=e.p.find('.stream-element').attr('src');
|
||||
image_data = new Image();
|
||||
image_data.src = url;
|
||||
finish(url,image_data,image_data.width,image_data.height);
|
||||
case'jpeg':
|
||||
url=e.p.find('.stream-element').attr('src')
|
||||
image_data = new Image()
|
||||
image_data.src = url
|
||||
finish(url,image_data,image_data.width,image_data.height)
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
url=e.p.find('.stream-element').attr('src');
|
||||
image_data = new Image();
|
||||
image_data.src = url;
|
||||
cb(url,image_data,image_data.width,image_data.height);
|
||||
url = e.p.find('.stream-element').attr('src')
|
||||
image_data = new Image()
|
||||
image_data.src = url
|
||||
cb(url,image_data,image_data.width,image_data.height)
|
||||
}
|
||||
}
|
||||
$.ccio.snapshotVideo=function(videoElement,cb){
|
||||
var image_data;
|
||||
$.ccio.snapshotVideo = function(videoElement,cb){
|
||||
var image_data
|
||||
var base64
|
||||
$('#temp').html('<canvas></canvas>')
|
||||
var c = $('#temp canvas')[0];
|
||||
var img = videoElement;
|
||||
c.width = img.videoWidth;
|
||||
c.height = img.videoHeight;
|
||||
var ctx = c.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0,c.width,c.height);
|
||||
var c = $('#temp canvas')[0]
|
||||
var img = videoElement
|
||||
c.width = img.videoWidth
|
||||
c.height = img.videoHeight
|
||||
var ctx = c.getContext('2d')
|
||||
ctx.drawImage(img, 0, 0,c.width,c.height)
|
||||
base64=c.toDataURL('image/jpeg')
|
||||
image_data=atob(base64.split(',')[1]);
|
||||
var arraybuffer = new ArrayBuffer(image_data.length);
|
||||
var view = new Uint8Array(arraybuffer);
|
||||
image_data=atob(base64.split(',')[1])
|
||||
var arraybuffer = new ArrayBuffer(image_data.length)
|
||||
var view = new Uint8Array(arraybuffer)
|
||||
for (var i=0; i<image_data.length; i++) {
|
||||
view[i] = image_data.charCodeAt(i) & 0xff;
|
||||
view[i] = image_data.charCodeAt(i) & 0xff
|
||||
}
|
||||
try {
|
||||
var blob = new Blob([arraybuffer], {type: 'application/octet-stream'});
|
||||
var blob = new Blob([arraybuffer], {type: 'application/octet-stream'})
|
||||
} catch (e) {
|
||||
var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder);
|
||||
bb.append(arraybuffer);
|
||||
var blob = bb.getBlob('application/octet-stream');
|
||||
var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder)
|
||||
bb.append(arraybuffer)
|
||||
var blob = bb.getBlob('application/octet-stream')
|
||||
}
|
||||
cb(base64,image_data,c.width,c.height);
|
||||
cb(base64,image_data,c.width,c.height)
|
||||
}
|
||||
$.ccio.magnifyStream = function(e){
|
||||
if(!e.p){
|
||||
e.e=$(this),
|
||||
e.p=e.e.parents('[mid]')
|
||||
$.ccio.magnifyStream = function(options,user){
|
||||
if(!user)user = $user
|
||||
if(!options.p && !options.parent){
|
||||
var el = $(this),
|
||||
parent = el.parents('[mid]')
|
||||
}else{
|
||||
parent = options.p || options.parent
|
||||
}
|
||||
if(e.animate === true){
|
||||
if(!options.attribute){
|
||||
options.attribute = ''
|
||||
}
|
||||
if(options.animate === true){
|
||||
var zoomGlassAnimate = 'animate'
|
||||
}else{
|
||||
var zoomGlassAnimate = 'css'
|
||||
}
|
||||
if(e.auto === true){
|
||||
if(!options.magnifyOffsetElement){
|
||||
options.magnifyOffsetElement = '.stream-block'
|
||||
}
|
||||
if(!options.targetForZoom){
|
||||
options.targetForZoom = '.stream-element'
|
||||
}
|
||||
if(options.auto === true){
|
||||
var streamBlockOperator = 'position'
|
||||
}else{
|
||||
var streamBlockOperator = 'offset'
|
||||
}
|
||||
if(e.useCanvas === true){
|
||||
var magnifiedElement = 'canvas'
|
||||
var magnifiedElement
|
||||
if(!options.videoUrl){
|
||||
if(options.useCanvas === true){
|
||||
magnifiedElement = 'canvas'
|
||||
}else{
|
||||
magnifiedElement = 'iframe'
|
||||
}
|
||||
}else{
|
||||
var magnifiedElement = 'iframe'
|
||||
magnifiedElement = 'video'
|
||||
}
|
||||
e.ke=e.p.attr('ke'),//group key
|
||||
e.mid=e.p.attr('mid'),//monitor id
|
||||
e.auth=e.p.attr('auth'),//authkey
|
||||
e.mon=$.ccio.mon[e.ke+e.mid+e.auth]//monitor configuration
|
||||
if(e.zoomAmount)e.mon.zoomAmount=3;
|
||||
if(!e.mon.zoomAmount)e.mon.zoomAmount=3;
|
||||
e.height=parseFloat(e.p.attr('realHeight')) * e.mon.zoomAmount//height of stream
|
||||
e.width=parseFloat(e.p.attr('realWidth')) * e.mon.zoomAmount;//width of stream
|
||||
var targetForZoom = e.p.find('.stream-element');
|
||||
zoomGlass = e.p.find(".zoomGlass");
|
||||
if(!options.mon && !options.monitor){
|
||||
var groupKey = parent.attr('ke')//group key
|
||||
var monitorId = parent.attr('mid')//monitor id
|
||||
var sessionKey = parent.attr('auth')//authkey
|
||||
var monitor = $.ccio.mon[groupKey + monitorId + sessionKey]//monitor configuration
|
||||
}else{
|
||||
var monitor = options.mon || options.monitor
|
||||
var groupKey = monitor.ke//group key
|
||||
var monitorId = monitor.mid//monitor id
|
||||
var sessionKey = monitor.auth//authkey
|
||||
}
|
||||
if(options.zoomAmount)zoomAmount = 3
|
||||
if(!zoomAmount)zoomAmount = 3
|
||||
var realHeight = parent.attr('realHeight')
|
||||
var realWidth = parent.attr('realWidth')
|
||||
var height = parseFloat(realHeight) * zoomAmount//height of stream
|
||||
var width = parseFloat(realWidth) * zoomAmount//width of stream
|
||||
var targetForZoom = parent.find(options.targetForZoom)
|
||||
zoomGlass = parent.find(".zoomGlass")
|
||||
var zoomFrame = function(){
|
||||
var magnify_offset = e.p.find('.stream-block')[streamBlockOperator]();
|
||||
var mx = e.pageX - magnify_offset.left;
|
||||
var my = e.pageY - magnify_offset.top;
|
||||
var rx = Math.round(mx/targetForZoom.width()*e.width - zoomGlass.width()/2)*-1;
|
||||
var ry = Math.round(my/targetForZoom.height()*e.height - zoomGlass.height()/2)*-1;
|
||||
var px = mx - zoomGlass.width()/2;
|
||||
var py = my - zoomGlass.height()/2;
|
||||
zoomGlass[zoomGlassAnimate]({left: px, top: py}).find(magnifiedElement)[zoomGlassAnimate]({left: rx, top: ry});
|
||||
var magnify_offset = parent.find(options.magnifyOffsetElement)[streamBlockOperator]()
|
||||
var mx = options.pageX - magnify_offset.left
|
||||
var my = options.pageY - magnify_offset.top
|
||||
var rx = Math.round(mx/targetForZoom.width()*width - zoomGlass.width()/2)*-1
|
||||
var ry = Math.round(my/targetForZoom.height()*height - zoomGlass.height()/2)*-1
|
||||
var px = mx - zoomGlass.width()/2
|
||||
var py = my - zoomGlass.height()/2
|
||||
zoomGlass[zoomGlassAnimate]({left: px, top: py}).find(magnifiedElement)[zoomGlassAnimate]({left: rx, top: ry})
|
||||
}
|
||||
if(!e.height||!e.width||zoomGlass.length===0){
|
||||
$.ccio.snapshot(e,function(url,buffer,width,height){
|
||||
e.width = width * e.mon.zoomAmount;
|
||||
e.height = height * e.mon.zoomAmount;
|
||||
e.p.attr('realWidth',width)
|
||||
e.p.attr('realHeight',height)
|
||||
zoomGlass = e.p.find(".zoomGlass");
|
||||
if(zoomGlass.length===0){
|
||||
if(e.useCanvas === true){
|
||||
e.p.append('<div class="zoomGlass"><canvas class="blenderCanvas"></canvas></div>');
|
||||
}else{
|
||||
e.p.append('<div class="zoomGlass"><iframe src="'+e.auth+'/embed/'+e.ke+'/'+e.mid+'/fullscreen|jquery|relative"/><div class="hoverShade"></div></div>');
|
||||
}
|
||||
zoomGlass = e.p.find(".zoomGlass");
|
||||
}
|
||||
zoomGlass.find(magnifiedElement).css({height:e.height,width:e.width});
|
||||
zoomFrame()
|
||||
var commit = function(height,width){
|
||||
zoomGlass.find(magnifiedElement).css({
|
||||
height: height,
|
||||
width: width
|
||||
})
|
||||
}else{
|
||||
zoomGlass.find(magnifiedElement).css({height:e.height,width:e.width});
|
||||
zoomFrame()
|
||||
}
|
||||
if(!height || !width || zoomGlass.length === 0){
|
||||
zoomGlass = parent.find(".zoomGlass")
|
||||
var zoomGlassShell = function(contents){return `<div ${options.attribute} class="zoomGlass">${contents}</div>`}
|
||||
if(!options.videoUrl){
|
||||
$.ccio.snapshot(monitor,function(url,buffer,w,h){
|
||||
parent.attr('realWidth',w)
|
||||
parent.attr('realHeight',h)
|
||||
if(zoomGlass.length === 0){
|
||||
if(options.useCanvas === true){
|
||||
parent.append(zoomGlassShell('<canvas class="blenderCanvas"></canvas>'))
|
||||
}else{
|
||||
parent.append(zoomGlassShell('<iframe src="'+$.ccio.init('location',user)+sessionKey+'/embed/'+groupKey+'/'+monitorId+'/fullscreen|jquery|relative"/><div class="hoverShade"></div>'))
|
||||
}
|
||||
zoomGlass = parent.find(".zoomGlass")
|
||||
}
|
||||
commit(h,w)
|
||||
})
|
||||
}else{
|
||||
if(zoomGlass.length === 0){
|
||||
parent.append(zoomGlassShell(`<video src="${options.videoUrl}" preload></video>`))
|
||||
}
|
||||
if(options.setTime){
|
||||
var video = zoomGlass.find('video')[0]
|
||||
video.currentTime = options.setTime
|
||||
height = video.videoHeight
|
||||
width = video.videoWidth
|
||||
parent.attr('realWidth',width)
|
||||
parent.attr('realHeight',height)
|
||||
}
|
||||
commit(height,width)
|
||||
}
|
||||
}else{
|
||||
if(options.setTime){
|
||||
var video = zoomGlass.find('video')
|
||||
var src = video.attr('src')
|
||||
video[0].currentTime = options.setTime
|
||||
if(options.videoUrl !== src)zoomGlass.html(`<video src="${options.videoUrl}" preload></video>`)
|
||||
}
|
||||
commit(height,width)
|
||||
}
|
||||
}
|
||||
$.ccio.destroyStream = function(d,user,killElement){
|
||||
if(d.mid && !d.id)d.id = d.mid
|
||||
console.log(d.ke+d.id+user.auth_token)
|
||||
console.log($.ccio.mon[d.ke+d.id+user.auth_token])
|
||||
if($.ccio.mon[d.ke+d.id+user.auth_token]){
|
||||
console.log('destroy')
|
||||
$.ccio.init('closeVideo',{mid:d.id,ke:d.ke},user);
|
||||
|
@ -297,3 +358,59 @@ $.ccio.destroyStream = function(d,user,killElement){
|
|||
}
|
||||
}
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$('body')
|
||||
.on('click', '.table-header-sorter', function () {
|
||||
var $sort = jQuery(this).find('i');
|
||||
var currentSort = undefined;
|
||||
if ($sort.hasClass('fa-sort-asc')) {
|
||||
currentSort = 'asc';
|
||||
} else if ($sort.hasClass('fa-sort-desc')) {
|
||||
currentSort = 'desc';
|
||||
}
|
||||
|
||||
jQuery(this)
|
||||
.parent()
|
||||
.find('i.fa')
|
||||
.removeClass('fa-sort-asc')
|
||||
.removeClass('fa-sort-desc')
|
||||
.addClass('fa-sort');
|
||||
|
||||
jQuery(this)
|
||||
.find('i.fa')
|
||||
.toggleClass('fa-sort', currentSort === 'desc')
|
||||
.toggleClass('fa-sort-asc', currentSort === undefined)
|
||||
.toggleClass('fa-sort-desc', currentSort === 'asc');
|
||||
|
||||
const field = jQuery(this).data('field');
|
||||
const $body = jQuery(this)
|
||||
.closest('.table')
|
||||
.find('tbody');
|
||||
|
||||
const sortedRows = $body
|
||||
.find('tr')
|
||||
.detach()
|
||||
.sort(function(a,b) {
|
||||
const data1 = jQuery(a).data('sort');
|
||||
const data2 = jQuery(b).data('sort');
|
||||
if (currentSort === undefined)
|
||||
return data1[field] > data2[field] ? 1 : data1[field] < data2[field] ? -1 : 0;
|
||||
else if (currentSort === 'asc')
|
||||
return data1[field] > data2[field] ? -1 : data1[field] < data2[field] ? 1 : 0;
|
||||
else
|
||||
return data1._no > data2._no ? 1 : data1._no < data2._no ? -1 : 0;
|
||||
});
|
||||
$body.append(sortedRows);
|
||||
})
|
||||
.on('click','[tab-chooser]',function(){
|
||||
var el = $(this)
|
||||
var parent = el.parents('[tab-chooser-parent]')
|
||||
var tabName = el.attr('tab-chooser')
|
||||
var allTabsInParent = parent.find('[tab-section]')
|
||||
var allTabChoosersInParent = parent.find('[tab-chooser]')
|
||||
allTabsInParent.hide()
|
||||
allTabChoosersInParent.removeClass('active')
|
||||
el.addClass('active')
|
||||
parent.find(`[tab-section="${tabName}"]`).show()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,8 +3,7 @@ $.ccio={
|
|||
mon:{},
|
||||
useUTC: <%- config.useUTC || false %>,
|
||||
definitions: <%-JSON.stringify(define)%>,
|
||||
libURL: '<%-window.libURL%>',
|
||||
logMemory: []
|
||||
libURL: '<%-window.libURL%>'
|
||||
};
|
||||
<% if(config.DropboxAppKey){ %>
|
||||
$.ccio.DropboxAppKey = '<%-window.DropboxAppKey%>'
|
||||
|
@ -118,7 +117,6 @@ $(document).ready(function(e){
|
|||
}
|
||||
})
|
||||
}
|
||||
//on window resize
|
||||
document.addEventListener("fullscreenchange", onFullScreenChange, false);
|
||||
document.addEventListener("webkitfullscreenchange", onFullScreenChange, false);
|
||||
document.addEventListener("mozfullscreenchange", onFullScreenChange, false);
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
$(document).ready(function(){
|
||||
$.confirm={e:$('#confirm_window')};
|
||||
$.confirm.title=$.confirm.e.find('.modal-title span')
|
||||
$.confirm.body=$.confirm.e.find('.modal-body')
|
||||
$.confirm.footer=$.confirm.e.find('.modal-footer')
|
||||
$.confirm.click=function(x,e){
|
||||
$.confirm.footer.find('.confirmaction').remove()
|
||||
var createButton = function(x,place,callback){
|
||||
$.confirm.footer.prepend('<button type="button" class="btn '+x.class+' confirmaction confirmaction'+place+'">'+x.title+'</button>')
|
||||
if(!x.class){x.class='btn-success'}
|
||||
if(!x.title){x.title='Save changes'}
|
||||
$.confirm.footer.find('.confirmaction'+place).click(function(){
|
||||
$.confirm.e.modal('hide')
|
||||
callback();
|
||||
})
|
||||
}
|
||||
if(x instanceof Array){
|
||||
$.each(x,function(place,x){
|
||||
createButton(x,place,x.callback)
|
||||
})
|
||||
}else{
|
||||
createButton(x,0,e)
|
||||
}
|
||||
}
|
||||
$.confirm.create = function(options){
|
||||
if(options.title && options.body){
|
||||
$.confirm.e.modal('show')
|
||||
$.confirm.title.text(options.title)
|
||||
$.confirm.body.html(options.body)
|
||||
}
|
||||
if(options.clickOptions && options.clickCallback)$.confirm.click(options.clickOptions,options.clickCallback)
|
||||
}
|
||||
$.confirm.e.on('hidden.bs.modal', function () {
|
||||
$.confirm.footer.find('.confirmaction').remove()
|
||||
})
|
||||
})
|
129
web/libs/js/dash2.downloadAndZip.js
Normal file
129
web/libs/js/dash2.downloadAndZip.js
Normal file
|
@ -0,0 +1,129 @@
|
|||
$(document).ready(function(){
|
||||
var downloadFile = function(remoteLink,onProgress,onSuccess){
|
||||
if(!onProgress)onProgress = function(){}
|
||||
if(!onSuccess)onSuccess = function(){}
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
xhr.addEventListener("progress", function(evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = (evt.loaded / evt.total * 100).toFixed(2);
|
||||
onProgress(percentComplete)
|
||||
}
|
||||
}, false)
|
||||
xhr.addEventListener('readystatechange', function(e) {
|
||||
if(xhr.readyState == 2 && xhr.status == 200) {
|
||||
// Download is being started
|
||||
}
|
||||
else if(xhr.readyState == 3) {
|
||||
// Download is under progress
|
||||
}
|
||||
else if(xhr.readyState == 4) {
|
||||
onSuccess(xhr.response)
|
||||
// Downloaing has finished
|
||||
|
||||
// request.response holds the file data
|
||||
}
|
||||
})
|
||||
xhr.responseType = 'blob'
|
||||
|
||||
// Downloading a JPEG file
|
||||
xhr.open('get', remoteLink)
|
||||
|
||||
xhr.send()
|
||||
}
|
||||
var downloadBulkVideos = function(videos,onProgress,onSuccess){
|
||||
var fileBuffers = {}
|
||||
var numberOfCompletedDownloads = 0
|
||||
var getVideo = function(video){
|
||||
var url = video.href
|
||||
downloadFile(url,function(percent){
|
||||
if(onProgress)onProgress(video,percent)
|
||||
},function(buffer){
|
||||
++numberOfCompletedDownloads
|
||||
fileBuffers[url] = Object.assign(video,{buffer: buffer})
|
||||
console.log(fileBuffers[url] )
|
||||
console.log(videos.length, numberOfCompletedDownloads)
|
||||
if(videos.length === numberOfCompletedDownloads){
|
||||
if(onSuccess)onSuccess(fileBuffers)
|
||||
}else{
|
||||
getVideo(videos[numberOfCompletedDownloads])
|
||||
}
|
||||
})
|
||||
}
|
||||
getVideo(videos[numberOfCompletedDownloads])
|
||||
}
|
||||
var saveFile = function(blob, filename) {
|
||||
console.log(blob,filename)
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
window.navigator.msSaveOrOpenBlob(blob, filename);
|
||||
} else {
|
||||
const a = document.createElement('a');
|
||||
document.body.appendChild(a);
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
var zipVideosAndDownload = function(videos,onSuccess){
|
||||
var zip = new JSZip();
|
||||
var zipFileName = `ShinobiVideos_${$.ccio.timeObject(new Date()).format('YYYY-MM-DDTHH-mm-ss')}.zip`
|
||||
var foldersCreated = {}
|
||||
var downloadBars = {}
|
||||
var progressBarHtml = []
|
||||
var videosCopy = Object.assign(videos,{})
|
||||
$.each(videosCopy,function(n,video){
|
||||
var fileZipName = `${$.ccio.timeObject(video.time).format('YYYY-MM-DDTHH-mm-ss')}.${video.ext}`
|
||||
var fileId = video.ke + video.mid + moment(video.time).format('YYYY-MM-DDTHH-mm-ss')
|
||||
video.fileId = fileId
|
||||
video.fileZipName = fileZipName
|
||||
if(!foldersCreated[video.ke]){
|
||||
foldersCreated[video.ke] = {}
|
||||
foldersCreated[video.ke]._zipFolder = zip.folder(video.ke)
|
||||
}
|
||||
if(!foldersCreated[video.ke][video.mid]){
|
||||
foldersCreated[video.ke][video.mid] = {}
|
||||
foldersCreated[video.ke][video.mid]._zipFolder = foldersCreated[video.ke]._zipFolder.folder(video.mid)
|
||||
}
|
||||
progressBarHtml.push(`<br><small></small><div id="download-${fileId}" class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div>`)
|
||||
})
|
||||
new PNotify({
|
||||
type:'notice',
|
||||
title: lang['Downloading Videos'],
|
||||
text: lang['Please Wait for Completion'] + progressBarHtml.join(''),
|
||||
hide: false,
|
||||
modules: {
|
||||
Buttons: {
|
||||
closer: true,
|
||||
sticker: false
|
||||
},
|
||||
Mobile: {
|
||||
swipeDismiss: false
|
||||
}
|
||||
},
|
||||
})
|
||||
$.each(videosCopy,function(n,video){
|
||||
downloadBars[video.fileId] = $(`#download-${video.fileId} .progress-bar`)
|
||||
})
|
||||
downloadBulkVideos(videosCopy,function(video,percent){
|
||||
downloadBars[video.fileId].width(percent + '%').attr('aria-valuenow', percent)
|
||||
},function(videosWithBuffers){
|
||||
new PNotify({
|
||||
type:'success',
|
||||
title: lang['Zipping Videos'],
|
||||
text: lang['Please Wait for Completion']
|
||||
})
|
||||
$.each(videosWithBuffers,function(n,video){
|
||||
foldersCreated[video.ke][video.mid]._zipFolder.file(video.fileZipName, video.buffer)
|
||||
})
|
||||
zip.generateAsync({type:"blob"}).then(function(content) {
|
||||
saveFile(content, zipFileName)
|
||||
if(onSuccess)onSuccess(content)
|
||||
});
|
||||
})
|
||||
}
|
||||
$.zipVideosAndDownload = zipVideosAndDownload
|
||||
})
|
|
@ -20,8 +20,13 @@ $.ccio.tm=function(x,d,z,user){
|
|||
d.per=parseInt(d.hr/24*100);
|
||||
d.circle='<div title="at '+d.hr+' hours of '+d.startMoment.format('MMMM DD')+'" '+href+' video="launch" class="progress-circle progress-'+d.per+'"><span>'+d.hr+'</span></div>'
|
||||
tmp+='<li class="video-item glM'+d.mid+user.auth_token+'" auth="'+user.auth_token+'" mid="'+d.mid+'" ke="'+d.ke+'" status="'+d.status+'" status="'+d.status+'" file="'+d.filename+'">'+d.circle+'<div><span title="'+d.endMoment.format()+'" class="livestamp"></span></div><div><div class="small"><b>'+lang.Start+'</b> : '+d.startMoment.format('h:mm:ss , MMMM Do YYYY')+'</div><div class="small"><b>'+lang.End+'</b> : '+d.endMoment.format('h:mm:ss , MMMM Do YYYY')+'</div></div><div><span class="pull-right">'+(parseInt(d.size)/1000000).toFixed(2)+'mb</span><div class="controls btn-group"><a class="btn btn-sm btn-primary" video="launch" '+href+'><i class="fa fa-play-circle"></i></a> <a download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-download"></i></a>'
|
||||
if($.ccio.DropboxAppKey){ tmp+='<a video="download" host="dropbox" download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-dropbox"></i></a>' }
|
||||
tmp+='<a title="'+lang['Delete Video']+'" video="delete" href="'+$.ccio.init('videoHrefToDelete',url)+'" class="btn btn-sm btn-danger permission_video_delete"><i class="fa fa-trash"></i></a></div></div></li>';
|
||||
if($.ccio.DropboxAppKey){
|
||||
tmp+='<a video="download" host="dropbox" download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-dropbox"></i></a>'
|
||||
}
|
||||
if($.ccio.permissionCheck('video_delete',d.mid)){
|
||||
tmp += '<a title="'+lang['Delete Video']+'" video="delete" href="'+$.ccio.init('videoHrefToDelete',url)+'" class="btn btn-sm btn-danger"><i class="fa fa-trash"></i></a>'
|
||||
}
|
||||
tmp += '</div></div></li>';
|
||||
$(z).each(function(n,v){
|
||||
v=$(v);
|
||||
if(v.find('.video-item').length>10){v.find('.video-item:last').remove()}
|
||||
|
@ -33,25 +38,25 @@ $.ccio.tm=function(x,d,z,user){
|
|||
tmp+='<div class="btn-group btn-group-xs">'
|
||||
var buttons = {
|
||||
"Pop": {
|
||||
"label": "Pop",
|
||||
"label": lang['Pop'],
|
||||
"attr": "monitor=\"pop\"",
|
||||
"class": "default",
|
||||
"icon": "external-link"
|
||||
},
|
||||
"Power Viewer": {
|
||||
"label": "Power Viewer",
|
||||
"attr": "monitor=\"powerview\"",
|
||||
"class": "default",
|
||||
"icon": "map-marker"
|
||||
},
|
||||
// "Power Viewer": {
|
||||
// "label": lang['Power Viewer'],
|
||||
// "attr": "monitor=\"powerview\"",
|
||||
// "class": "default",
|
||||
// "icon": "map-marker"
|
||||
// },
|
||||
"Videos List": {
|
||||
"label": "Videos List",
|
||||
"label": lang['Videos List'],
|
||||
"attr": "monitor=\"videos_table\"",
|
||||
"class": "default",
|
||||
"icon": "film"
|
||||
},
|
||||
"Monitor Settings": {
|
||||
"label": "Monitor Settings",
|
||||
"label": lang['Monitor Settings'],
|
||||
"attr": "monitor=\"edit\"",
|
||||
"class": "default",
|
||||
"icon": "wrench"
|
||||
|
@ -89,50 +94,56 @@ $.ccio.tm=function(x,d,z,user){
|
|||
tmp+='<div class="btn-group btn-group-sm">'//start of btn list
|
||||
var buttons = {
|
||||
"Snapshot": {
|
||||
"label": "Snapshot",
|
||||
"label": lang['Snapshot'],
|
||||
"attr": "monitor=\"snapshot\"",
|
||||
"class": "primary",
|
||||
"icon": "camera"
|
||||
},
|
||||
"Show Logs": {
|
||||
"label": "Show Logs",
|
||||
"label": lang['Show Logs'],
|
||||
"attr": "monitor=\"show_data\"",
|
||||
"class": "warning",
|
||||
"icon": "exclamation-triangle"
|
||||
},
|
||||
"Control": {
|
||||
"label": "Control",
|
||||
"label": lang['Control'],
|
||||
"attr": "monitor=\"control_toggle\"",
|
||||
"class": "default arrows",
|
||||
"icon": "arrows"
|
||||
},
|
||||
"Status Indicator": {
|
||||
"label": "Status Indicator",
|
||||
"Reconnect Stream": {
|
||||
"label": lang['Reconnect Stream'],
|
||||
"attr": "monitor=\"watch_on\"",
|
||||
"class": "success signal",
|
||||
"icon": "plug"
|
||||
},
|
||||
"Pop": {
|
||||
"label": "Pop",
|
||||
"label": lang['Pop'],
|
||||
"attr": "monitor=\"pop\"",
|
||||
"class": "default",
|
||||
"icon": "external-link"
|
||||
},
|
||||
"Pop": {
|
||||
"label": lang['Zoom In'],
|
||||
"attr": "monitor=\"zoomStreamWithMouse\"",
|
||||
"class": "default",
|
||||
"icon": "search-plus"
|
||||
},
|
||||
"Calendar": {
|
||||
"label": "Calendar",
|
||||
"label": lang['Calendar'],
|
||||
"attr": "monitor=\"calendar\"",
|
||||
"class": "default ",
|
||||
"icon": "calendar"
|
||||
},
|
||||
"Power Viewer": {
|
||||
"label": "Power Viewer",
|
||||
"attr": "monitor=\"powerview\"",
|
||||
"class": "default",
|
||||
"icon": "map-marker"
|
||||
},
|
||||
// "Power Viewer": {
|
||||
// "label": lang['Power Viewer'],
|
||||
// "attr": "monitor=\"powerview\"",
|
||||
// "class": "default",
|
||||
// "icon": "map-marker"
|
||||
// },
|
||||
"Time-lapse": {
|
||||
"label": "Time-lapse",
|
||||
"attr": "monitor=\"timelapse\"",
|
||||
"label": lang['Time-lapse'],
|
||||
"attr": "monitor=\"timelapseJpeg\"",
|
||||
"class": "default",
|
||||
"icon": "angle-double-right"
|
||||
},
|
||||
|
@ -143,25 +154,25 @@ $.ccio.tm=function(x,d,z,user){
|
|||
"icon": "th"
|
||||
},
|
||||
"Videos List": {
|
||||
"label": "Videos List",
|
||||
"label": lang['Videos List'],
|
||||
"attr": "monitor=\"videos_table\"",
|
||||
"class": "default",
|
||||
"icon": "film"
|
||||
},
|
||||
"Monitor Settings": {
|
||||
"label": "Monitor Settings",
|
||||
"label": lang['Monitor Settings'],
|
||||
"attr": "monitor=\"edit\"",
|
||||
"class": "default",
|
||||
"icon": "wrench"
|
||||
},
|
||||
"Fullscreen": {
|
||||
"label": "Fullscreen",
|
||||
"label": lang['Fullscreen'],
|
||||
"attr": "monitor=\"fullscreen\"",
|
||||
"class": "default",
|
||||
"icon": "arrows-alt"
|
||||
},
|
||||
"Close": {
|
||||
"label": "Close",
|
||||
"label": lang['Close'],
|
||||
"attr": "monitor=\"watch_off\"",
|
||||
"class": "danger",
|
||||
"icon": "times"
|
||||
|
|
|
@ -75,7 +75,7 @@ $(document).ready(function(e){
|
|||
$.logWriter.floodTimeout = setTimeout(function(){
|
||||
delete($.logWriter.floodTimeout)
|
||||
$.logWriter.floodCounter = 0
|
||||
},1000)
|
||||
},2000)
|
||||
$.ccio.tm(4,d,'#logs,'+id+'.monitor_item .logs:visible,'+id+'#add_monitor:visible .logs',user)
|
||||
}
|
||||
//open all monitors
|
||||
|
@ -320,6 +320,32 @@ $(document).ready(function(e){
|
|||
user=$user
|
||||
}
|
||||
switch(e.a){
|
||||
case'zoomStreamWithMouse':
|
||||
var streamWindow = $('.monitor_item[mid="'+e.mid+'"][ke="'+e.ke+'"][auth="'+e.auth+'"]')
|
||||
if(e.mon.magnifyStreamEnabled){
|
||||
e.mon.magnifyStreamEnabled = false
|
||||
streamWindow
|
||||
.off('mousemove')
|
||||
.off('touchmove')
|
||||
.find('.zoomGlass').remove()
|
||||
}else{
|
||||
e.mon.magnifyStreamEnabled = true
|
||||
const magnifyStream = function(e){
|
||||
$.ccio.magnifyStream({
|
||||
p: streamWindow,
|
||||
zoomAmount: 1,
|
||||
auto: false,
|
||||
animate: false,
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY,
|
||||
attribute: 'monitor="zoomStreamWithMouse"'
|
||||
},user)
|
||||
}
|
||||
streamWindow
|
||||
.on('mousemove', magnifyStream)
|
||||
.on('touchmove', magnifyStream)
|
||||
}
|
||||
break;
|
||||
case'show_data':
|
||||
e.p.toggleClass('show_data')
|
||||
var dataBlocks = e.p.find('.stream-block,.mdl-data_window')
|
||||
|
@ -365,31 +391,17 @@ $(document).ready(function(e){
|
|||
})
|
||||
}
|
||||
break;
|
||||
case'timelapse':
|
||||
$.timelapse.e.modal('show')
|
||||
$.timelapse.monitors.find('.monitor').remove()
|
||||
case'timelapseJpeg':
|
||||
$.timelapseJpeg.e.modal('show')
|
||||
$.timelapseJpeg.monitors.find('.monitor').remove()
|
||||
$.each($.ccio.mon,function(n,v){
|
||||
$.timelapse.monitors.append('<option class="monitor" value="'+v.mid+'">'+v.name+'</option>')
|
||||
$.timelapseJpeg.monitors.append('<option class="monitor" value="'+v.mid+'">'+v.name+'</option>')
|
||||
})
|
||||
e.e=$.timelapse.monitors.find('.monitor').prop('selected',false)
|
||||
e.e=$.timelapseJpeg.monitors.find('.monitor').prop('selected',false)
|
||||
if(e.mid!==''){
|
||||
e.e=$.timelapse.monitors.find('.monitor[value="'+e.mid+'"]')
|
||||
e.e=$.timelapseJpeg.monitors.find('.monitor[value="'+e.mid+'"]')
|
||||
}
|
||||
e.e.first().prop('selected',true)
|
||||
$.timelapse.f.submit()
|
||||
break;
|
||||
case'powerview':
|
||||
$.pwrvid.e.modal('show')
|
||||
$.pwrvid.m.empty()
|
||||
$.each($.ccio.mon,function(n,v){
|
||||
$.pwrvid.m.append('<option value="'+v.mid+'">'+v.name+'</option>')
|
||||
})
|
||||
e.e=$.pwrvid.m.find('option').prop('selected',false)
|
||||
if(e.mid!==''){
|
||||
e.e=$.pwrvid.m.find('[value="'+e.mid+'"]')
|
||||
}
|
||||
e.e.first().prop('selected',true)
|
||||
$.pwrvid.f.submit()
|
||||
break;
|
||||
case'region':
|
||||
if(!e.mon){
|
||||
|
@ -508,6 +520,7 @@ $(document).ready(function(e){
|
|||
right: 'month,agendaWeek,agendaDay,listWeek'
|
||||
},
|
||||
defaultDate: $.ccio.timeObject(d.videos[0].time).format('YYYY-MM-DD'),
|
||||
locale: user.details.lang.substring(0, 2),
|
||||
navLinks: true,
|
||||
eventLimit: true,
|
||||
events:e.ar,
|
||||
|
@ -562,21 +575,21 @@ $(document).ready(function(e){
|
|||
var showThumbnail = $.ccio.op().showThumbnail === '1'
|
||||
$.vidview.e.removeClass('dark')
|
||||
e.t.attr('class','fa fa-film')
|
||||
var tmp = '<table class="table table-striped" style="max-height:500px">';
|
||||
var tmp = '<table class="table table-striped table-bordered">';
|
||||
tmp+='<thead>';
|
||||
tmp+='<tr>';
|
||||
tmp+='<th><div class="checkbox"><input id="videos_select_all" type="checkbox"><label for="videos_select_all"></label></div></th>';
|
||||
if(showThumbnail)tmp+='<th data-field="Thumbnail" data-sortable="true">'+lang.Thumbnail+'</th>';
|
||||
tmp+='<th data-field="Closed" data-sortable="true">'+lang.Closed+'</th>';
|
||||
tmp+='<th data-field="Ended" data-sortable="true">'+lang.Ended+'</th>';
|
||||
tmp+='<th data-field="Started" data-sortable="true">'+lang.Started+'</th>';
|
||||
tmp+='<th data-field="Monitor" data-sortable="true">'+lang.Monitor+'</th>';
|
||||
tmp+='<th data-field="Filename" data-sortable="true">'+lang.Filename+'</th>';
|
||||
tmp+='<th data-field="Size" data-sortable="true">'+lang['Size (mb)']+'</th>';
|
||||
tmp+='<th data-field="Preview" data-sortable="true">'+lang.Preview+'</th>';
|
||||
tmp+='<th data-field="Watch" data-sortable="true">'+lang.Watch+'</th>';
|
||||
tmp+='<th data-field="Download" data-sortable="true">'+lang.Download+'</th>';
|
||||
tmp+='<th class="permission_video_delete" data-field="Delete" data-sortable="true">'+lang.Delete+'</th>';
|
||||
if(showThumbnail)tmp+='<th>'+lang.Thumbnail+'</th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Closed">'+lang.Closed+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Ended">'+lang.Ended+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Started">'+lang.Started+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Monitor">'+lang.Monitor+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Filename">'+lang.Filename+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th class="table-header-sorter" data-field="Size">'+lang['Size (mb)']+'<i class="fa fa-sort"></i></th>';
|
||||
tmp+='<th>'+lang.Preview+'</th>';
|
||||
tmp+='<th>'+lang.Watch+'</th>';
|
||||
tmp+='<th>'+lang.Download+'</th>';
|
||||
tmp+='<th class="permission_video_delete">'+lang.Delete+'</th>';
|
||||
// tmp+='<th class="permission_video_delete" data-field="Fix" data-sortable="true">'+lang.Fix+'</th>';
|
||||
tmp+='</tr>';
|
||||
tmp+='</thead>';
|
||||
|
@ -588,10 +601,19 @@ $(document).ready(function(e){
|
|||
v.mon=$.ccio.mon[v.ke+v.mid+user.auth_token];
|
||||
v.start=v.time;
|
||||
// v.filename=$.ccio.init('tf',v.time)+'.'+v.ext;
|
||||
tmp+='<tr data-ke="'+v.ke+'" data-status="'+v.status+'" data-mid="'+v.mid+'" data-file="'+v.filename+'" data-auth="'+v.mon.user.auth_token+'">';
|
||||
const sortData = {
|
||||
_no: n,
|
||||
Closed: $.ccio.timeObject(v.end).unix(),
|
||||
Ended: $.ccio.timeObject(v.end).unix(),
|
||||
Started: $.ccio.timeObject(v.time).unix(),
|
||||
Monitor: v.mon.name,
|
||||
Filename: v.filename,
|
||||
Size: v.size,
|
||||
};
|
||||
tmp+='<tr data-sort="' + JSON.stringify(sortData).replace(/"/g, """) + '" data-ke="'+v.ke+'" data-status="'+v.status+'" data-mid="'+v.mid+'" data-file="'+v.filename+'" data-auth="'+v.mon.user.auth_token+'">';
|
||||
tmp+='<td><div class="checkbox"><input id="'+v.ke+'_'+v.filename+'" name="'+v.filename+'" value="'+v.mid+'" type="checkbox"><label for="'+v.ke+'_'+v.filename+'"></label></div></td>';
|
||||
if(showThumbnail)tmp+='<td class="text-center"><img class="thumbnail"></td>';
|
||||
tmp+='<td><span class="livestamp" title="'+$.ccio.timeObject(v.end).format('YYYY-MM-DD HH:mm:ss')+'"></span></td>';
|
||||
tmp+='<td>'+$.ccio.timeObject(v.end).fromNow()+'</td>';
|
||||
tmp+='<td title="'+v.end+'">'+$.ccio.timeObject(v.end).format('h:mm:ss A, MMMM Do YYYY')+'</td>';
|
||||
tmp+='<td title="'+v.time+'">'+$.ccio.timeObject(v.time).format('h:mm:ss A, MMMM Do YYYY')+'</td>';
|
||||
tmp+='<td>'+v.mon.name+'</td>';
|
||||
|
@ -608,6 +630,10 @@ $(document).ready(function(e){
|
|||
tmp+='</tbody>';
|
||||
tmp+='</table>';
|
||||
e.b.html(tmp)
|
||||
e.b.css({
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
}).scrollTop(0);
|
||||
if(showThumbnail){
|
||||
var i = 0
|
||||
var getThumbnail = function(){
|
||||
|
@ -624,8 +650,6 @@ $(document).ready(function(e){
|
|||
}
|
||||
getThumbnail()
|
||||
}
|
||||
$.ccio.init('ls');
|
||||
$.vidview.e.find('table').bootstrapTable();
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
@ -796,6 +820,4 @@ $(document).ready(function(e){
|
|||
.on('dblclick','.stream-hud',function(){
|
||||
$(this).parents('[mid]').find('[monitor="fullscreen"]').click();
|
||||
})
|
||||
//.on('mousemove',".magnifyStream",$.ccio.magnifyStream)
|
||||
//.on('touchmove',".magnifyStream",$.ccio.magnifyStream);
|
||||
})
|
||||
|
|
|
@ -336,11 +336,11 @@ $.ccio.init=function(x,d,user,k){
|
|||
case'drawMatrices':
|
||||
d.height=d.stream.height()
|
||||
d.width=d.stream.width()
|
||||
if(d.monitorDetails.detector_scale_x===''){d.monitorDetails.detector_scale_x=320}
|
||||
if(d.monitorDetails.detector_scale_y===''){d.monitorDetails.detector_scale_y=240}
|
||||
if(!d.details.imgWidth && d.monitorDetails.detector_scale_x===''){d.monitorDetails.detector_scale_x=320}
|
||||
if(!d.details.imgHeight && d.monitorDetails.detector_scale_y===''){d.monitorDetails.detector_scale_y=240}
|
||||
|
||||
d.widthRatio=d.width/d.monitorDetails.detector_scale_x
|
||||
d.heightRatio=d.height/d.monitorDetails.detector_scale_y
|
||||
d.widthRatio=d.width/d.details.imgWidth || d.monitorDetails.detector_scale_x
|
||||
d.heightRatio=d.height/d.details.imgHeight || d.monitorDetails.detector_scale_y
|
||||
|
||||
d.streamObjects.find('.stream-detected-object[name="'+d.details.name+'"]').remove()
|
||||
d.tmp=''
|
||||
|
|
320
web/libs/js/dash2.monitorStates.js
Normal file
320
web/libs/js/dash2.monitorStates.js
Normal file
|
@ -0,0 +1,320 @@
|
|||
$(document).ready(function(){
|
||||
$.monitorStates = {
|
||||
e: $('#monitorStates'),
|
||||
selector: $('#monitorStatesSelector'),
|
||||
monitors: $('#monitorStatesMonitors'),
|
||||
loaded: {}
|
||||
}
|
||||
$.monitorStates.f = $.monitorStates.e.find('form')
|
||||
$.monitorStates.loadPresets = function(callback){
|
||||
$.get($.ccio.init('location',$user) + $user.auth_token + '/monitorStates/' + $user.ke,function(d){
|
||||
var html = ''
|
||||
$.each(d.presets,function(n,v){
|
||||
$.monitorStates.loaded[v.name] = v
|
||||
html += '<option value="' + v.name + '">' + v.name + '</option>'
|
||||
})
|
||||
$.monitorStates.selector.find('optgroup').html(html)
|
||||
if(callback)callback()
|
||||
})
|
||||
}
|
||||
$.monitorStates.e.on('shown.bs.modal', function (e) {
|
||||
if($.monitorStates.selector.val() === '')$.monitorStates.loadPresets()
|
||||
})
|
||||
var buildOptions = function(field,possiblities,fieldData){
|
||||
if(!field)console.error('field',field)
|
||||
var fieldElement = ''
|
||||
possiblities.forEach(function(option){
|
||||
if(option.optgroup){
|
||||
fieldElement += '<optgroup label="' + option.name + '">'
|
||||
fieldElement += buildOptions(field,option.optgroup,fieldData)
|
||||
fieldElement += '</optgroup>'
|
||||
}else{
|
||||
var selected = ''
|
||||
if(option.value === fieldData || option.value === field.default){
|
||||
selected = 'selected'
|
||||
}
|
||||
fieldElement += '<option value="' + option.value + '" ' + selected + '>' + option.name + '</option>'
|
||||
}
|
||||
})
|
||||
return fieldElement
|
||||
}
|
||||
var drawBlock = function(monitorSettings,preloadedData){
|
||||
var html = ''
|
||||
var headerTitle = lang[monitorSettings.name]
|
||||
// if(monitorSettings.evaluation && !eval(monitorSettings.evaluation)){
|
||||
// return
|
||||
// }
|
||||
if(monitorSettings.blocks){
|
||||
monitorSettings.blocks.forEach(function(settingsBlock){
|
||||
html += drawBlock(settingsBlock)
|
||||
})
|
||||
}
|
||||
if(monitorSettings.info){
|
||||
monitorSettings.info.forEach(function(field){
|
||||
if(field.isFormGroupGroup === true){
|
||||
html += drawBlock(field)
|
||||
}else{
|
||||
if(field.notForSubAccount === true){
|
||||
var notForSubAccount = '!details.sub'
|
||||
if(!field.evaluation){
|
||||
field.evaluation = notForSubAccount
|
||||
}else{
|
||||
field.evaluation += ' && ' + notForSubAccount
|
||||
}
|
||||
}
|
||||
if(!field.name || field.evaluation && !eval(field.evaluation)){
|
||||
return
|
||||
}
|
||||
var attributes = []
|
||||
var listGroupClass = []
|
||||
var monitorRowFieldClass = []
|
||||
if(preloadedData){
|
||||
var isDetail = false
|
||||
var name = field.name
|
||||
var fieldData
|
||||
if(name.indexOf('detail=') > -1){
|
||||
isDetail = true
|
||||
name = name.replace('detail=','')
|
||||
if(preloadedData.details)fieldData = preloadedData.details[name]
|
||||
}else{
|
||||
fieldData = preloadedData[name]
|
||||
}
|
||||
if(fieldData){
|
||||
listGroupClass.push('active')
|
||||
if(fieldType !== 'select'){
|
||||
attributes.push(`value="${fieldData}"`)
|
||||
}
|
||||
}else{
|
||||
monitorRowFieldClass.push('display:none')
|
||||
}
|
||||
}else{
|
||||
monitorRowFieldClass.push('display:none')
|
||||
}
|
||||
if(field.placeholder || field.default){
|
||||
attributes.push(`placeholder="${field.placeholder || field.default}"`)
|
||||
}else if(field.example){
|
||||
attributes.push(`placeholder="Example : ${field.example}"`)
|
||||
}
|
||||
var possiblities = field.possible || []
|
||||
var fieldType = field.fieldType || 'text'
|
||||
var fieldElement = ''
|
||||
switch(fieldType){
|
||||
case'number':
|
||||
if(field.numberMin){
|
||||
attributes.push(`min="${field.numberMin}"`)
|
||||
}
|
||||
if(field.numberMax){
|
||||
attributes.push(`max="${field.numberMax}"`)
|
||||
}
|
||||
fieldElement = '<div><input type="number" class="form-control" ' + attributes.join(' ') + '></div>'
|
||||
break;
|
||||
case'password':
|
||||
fieldElement = '<div><input type="password" class="form-control" ' + attributes.join(' ') + '></div>'
|
||||
break;
|
||||
case'text':
|
||||
fieldElement = `<div><input class="form-control" ${attributes.join(' ')}></div>`
|
||||
break;
|
||||
case'textarea':
|
||||
fieldElement = '<div><textarea class="form-control" ' + attributes.join(' ') + '></textarea></div>'
|
||||
break;
|
||||
case'select':
|
||||
fieldElement = '<div><select class="form-control" ' + attributes.join(' ') + '>'
|
||||
fieldElement += buildOptions(field,possiblities,fieldData)
|
||||
fieldElement += '</select></div>'
|
||||
break;
|
||||
}
|
||||
if(fieldType === 'ul' || fieldType === 'div' || fieldType === 'btn' || field.name === 'mid'){
|
||||
|
||||
}else{
|
||||
if(headerTitle){
|
||||
html += `<div class="list-group-item ${listGroupClass.join(' ')}" data-name="${field.name}" data-value="${field.value || ""}">
|
||||
<h4>${headerTitle} : ${field.field}</h4>
|
||||
<div><small>${field.description}</small></div>
|
||||
<div class="state-monitor-row-fields-field mt-4" style="${monitorRowFieldClass.join(' ')}">${fieldElement}</div>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return html
|
||||
}
|
||||
$.monitorStates.drawMonitor = function(preloadedData){
|
||||
var MonitorSettings = $.ccio.definitions['Monitor Settings']
|
||||
var html = ''
|
||||
console.log(preloadedData)
|
||||
Object.keys(MonitorSettings.blocks).forEach(function(blockKey){
|
||||
var block = MonitorSettings.blocks[blockKey]
|
||||
html += drawBlock(block,preloadedData)
|
||||
})
|
||||
var monitorSelect = `<select class="state-monitor-row-select form-control">`
|
||||
$.each($.ccio.mon,function(n,monitor){
|
||||
monitorSelect += `<option value="${monitor.mid}">${monitor.name} (${monitor.mid})</option>`
|
||||
})
|
||||
monitorSelect += `</select>`
|
||||
var fullHtml = `<div class="form-group state-monitor-row">
|
||||
<div class="input-group state-monitor-row-select-container">
|
||||
${monitorSelect}
|
||||
<div class="input-group-btn">
|
||||
<a class="btn btn-danger delete-monitor">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group state-monitor-row-fields-container" style="height:300px;overflow: auto">
|
||||
${html}
|
||||
</div>
|
||||
</div>`
|
||||
return fullHtml
|
||||
}
|
||||
$.monitorStates.e.on('click','.add-monitor',function(e){
|
||||
var el = $(this)
|
||||
var html = $.monitorStates.drawMonitor()
|
||||
$.monitorStates.monitors.append(html)
|
||||
})
|
||||
$.monitorStates.e.on('click','.state-monitor-row-fields-container .list-group-item',function(e){
|
||||
var el = $(this)
|
||||
var listGroupParent = el.parents('.list-group')
|
||||
var fieldContainer = el.find('.state-monitor-row-fields-field')
|
||||
var name = el.attr('data-name')
|
||||
var value = el.attr('data-value')
|
||||
if(el.hasClass('active')){
|
||||
el.removeClass('active')
|
||||
fieldContainer.hide()
|
||||
}else{
|
||||
el.addClass('active')
|
||||
fieldContainer.show()
|
||||
}
|
||||
})
|
||||
$.monitorStates.e.on('click','.state-monitor-row-fields-container .form-control',function(e){
|
||||
e.preventDefault()
|
||||
return false
|
||||
})
|
||||
$.monitorStates.e.on('change','.json',function(e){
|
||||
var el = $(this)
|
||||
var val = el.val()
|
||||
try{
|
||||
el.css('border-color','green')
|
||||
var parsed = JSON.parse(val)
|
||||
el.val(JSON.stringify(parsed,null,3))
|
||||
}catch(err){
|
||||
el.css('border-color','red')
|
||||
return $.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'})
|
||||
}
|
||||
})
|
||||
$.monitorStates.e.on('click','.delete',function(e){
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.text(lang['Delete Monitor States Preset']);
|
||||
$.confirm.body.html(lang.deleteMonitorStateText1);
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
var form = $.monitorStates.f.serializeObject()
|
||||
$.post($.ccio.init('location',$user) + $user.auth_token + '/monitorStates/' + $user.ke + '/' + form.name + '/delete',function(d){
|
||||
$.ccio.log(d)
|
||||
if(d.ok === true){
|
||||
$.monitorStates.loadPresets()
|
||||
$.ccio.init('note',{title:lang.Success,text:d.msg,type:'success'})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
$.monitorStates.e.on('click','.delete-monitor',function(e){
|
||||
var el = $(this).parents('.state-monitor-row')
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.text(lang['Delete Monitor State']);
|
||||
$.confirm.body.html(lang.deleteMonitorStateText2)
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
el.remove()
|
||||
})
|
||||
})
|
||||
$.monitorStates.selector.change(function(e){
|
||||
var selected = $(this).val()
|
||||
var loaded = $.monitorStates.loaded[selected]
|
||||
var namespace = $.monitorStates.e.find('[name="name"]')
|
||||
var deleteButton = $.monitorStates.e.find('.delete')
|
||||
if(loaded){
|
||||
namespace.val(loaded.name)
|
||||
var html = ''
|
||||
$.each(loaded.details.monitors,function(n,v){
|
||||
console.log(v)
|
||||
html += $.monitorStates.drawMonitor(v)
|
||||
})
|
||||
$.monitorStates.monitors.html(html)
|
||||
deleteButton.show()
|
||||
}else{
|
||||
namespace.val('')
|
||||
$.monitorStates.monitors.empty()
|
||||
deleteButton.hide()
|
||||
}
|
||||
})
|
||||
$.monitorStates.getFormValuesFromJson = function(){
|
||||
var rows = $.monitorStates.monitors.find('.state-monitor-row')
|
||||
var monitors = []
|
||||
rows.each(function(n,v){
|
||||
var el = $(v)
|
||||
var textarea = el.find('textarea')
|
||||
try{
|
||||
var json = JSON.parse(el.find('.json').val())
|
||||
textarea.css('border-color','green')
|
||||
if(json.mid)monitors.push(json)
|
||||
}catch(err){
|
||||
textarea.css('border-color','red')
|
||||
$.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'})
|
||||
}
|
||||
})
|
||||
return monitors
|
||||
}
|
||||
$.monitorStates.getFormValues = function(){
|
||||
var rows = $.monitorStates.monitors.find('.state-monitor-row')
|
||||
var monitors = []
|
||||
rows.each(function(n,v){
|
||||
var monitorJson = {
|
||||
details: {}
|
||||
}
|
||||
var el = $(v)
|
||||
var fieldsSelcted = el.find('.list-group-item.active')
|
||||
monitorJson.mid = el.find('.state-monitor-row-select').val()
|
||||
fieldsSelcted.each(function(nn,element){
|
||||
var field = $(element)
|
||||
var name = field.attr('data-name')
|
||||
var value = field.find('.form-control').val()
|
||||
var isDetail = false
|
||||
if(name.indexOf('detail=') > -1){
|
||||
isDetail = true
|
||||
name = name.replace('detail=','')
|
||||
monitorJson.details[name] = value
|
||||
}else{
|
||||
monitorJson[name] = value
|
||||
}
|
||||
})
|
||||
if(Object.keys(monitorJson).length > 2 || Object.keys(monitorJson.details).length > 2){
|
||||
monitors.push(monitorJson)
|
||||
}
|
||||
})
|
||||
return monitors
|
||||
}
|
||||
$.monitorStates.f.submit(function(e){
|
||||
e.preventDefault()
|
||||
var el = $(this)
|
||||
var form = el.serializeObject()
|
||||
var monitors = $.monitorStates.getFormValues()
|
||||
if(form.name === ''){
|
||||
return $.ccio.init('note',{title:lang['Invalid Data'],text:lang['Name cannot be empty.'],type:'error'})
|
||||
}
|
||||
if(monitors.length === 0){
|
||||
return $.ccio.init('note',{title:lang['Invalid Data'],text:lang['Must be atleast one row'],type:'error'})
|
||||
}
|
||||
var data = {
|
||||
monitors: monitors
|
||||
}
|
||||
$.post($.ccio.init('location',$user) + $user.auth_token + '/monitorStates/' + $user.ke + '/' + form.name + '/insert',{data:data},function(d){
|
||||
$.ccio.log(d)
|
||||
if(d.ok === true){
|
||||
$.monitorStates.loadPresets(function(){
|
||||
$.monitorStates.selector.val(form.name)
|
||||
})
|
||||
$.ccio.init('note',{title:lang.Success,text:d.msg,type:'success'})
|
||||
}
|
||||
})
|
||||
return false;
|
||||
})
|
||||
})
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,17 @@
|
|||
$(document).ready(function(e){
|
||||
var getUrlPieces = function(url){
|
||||
var el = document.createElement('a');
|
||||
el.href = url
|
||||
return el
|
||||
// el.host // www.somedomain.com (includes port if there is one[1])
|
||||
// el.hostname // www.somedomain.com
|
||||
// el.hash // #top
|
||||
// el.href // http://www.somedomain.com/account/search?filter=a#top
|
||||
// el.pathname // /account/search
|
||||
// el.port // (port if there is one[1])
|
||||
// el.protocol // http:
|
||||
// el.search // ?filter=a
|
||||
}
|
||||
//multi monitor manager
|
||||
$.multimon={e:$('#multi_mon')};
|
||||
$.multimon.table=$.multimon.e.find('.tableData tbody');
|
||||
|
@ -17,7 +30,7 @@ $.multimon.e.find('.import_config').click(function(){
|
|||
var e={};e.e=$(this);e.mid=e.e.parents('[mid]').attr('mid');
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.text(lang['Import Monitor Configuration'])
|
||||
e.html=lang.ImportMultiMonitorConfigurationText+'<div style="margin-top:15px"><div class="form-group"><textarea placeholder="'+lang['Paste JSON here.']+'" class="form-control"></textarea></div><label class="upload_file btn btn-primary btn-block"> Upload File <input class="upload" type=file name="files[]"></label></div>';
|
||||
e.html=lang.ImportMultiMonitorConfigurationText+'<div style="margin-top:15px"><div class="form-group"><textarea placeholder="'+lang['Paste JSON here.']+'" class="form-control"></textarea></div><label class="upload_file btn btn-primary btn-block">'+lang['Upload File']+'<input class="upload" type=file name="files[]"></label></div>';
|
||||
$.confirm.body.html(e.html)
|
||||
$.confirm.e.find('.upload').change(function(e){
|
||||
var files = e.target.files; // FileList object
|
||||
|
@ -28,7 +41,7 @@ $.multimon.e.find('.import_config').click(function(){
|
|||
}
|
||||
reader.readAsText(f);
|
||||
});
|
||||
$.confirm.click({title:'Import',class:'btn-primary'},function(){
|
||||
$.confirm.click({title:lang['Import'],class:'btn-primary'},function(){
|
||||
// setTimeout(function(){
|
||||
// $.confirm.e.modal('show');
|
||||
// },1000)
|
||||
|
@ -67,7 +80,8 @@ $.multimon.e.find('.import_config').click(function(){
|
|||
console.log(newMon)
|
||||
return newMon
|
||||
}
|
||||
parsedData=JSON.parse($.confirm.e.find('textarea').val());
|
||||
var textFieldData = $.confirm.e.find('textarea').val()
|
||||
var parsedData = JSON.parse(textFieldData)
|
||||
//zoneminder one monitor
|
||||
if(parsedData.monitor){
|
||||
$.aM.import({
|
||||
|
@ -95,8 +109,45 @@ $.multimon.e.find('.import_config').click(function(){
|
|||
})
|
||||
}
|
||||
}catch(err){
|
||||
$.ccio.log(err)
|
||||
$.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'})
|
||||
//#EXTM3U
|
||||
if(textFieldData.indexOf('#EXTM3U') > -1 && textFieldData.indexOf('{"') === -1){
|
||||
var m3u8List = textFieldData.replace('#EXTM3U','').trim().split('\n')
|
||||
var parsedList = {}
|
||||
var currentName
|
||||
m3u8List.forEach(function(line){
|
||||
if(line.indexOf('#EXTINF:-1,') > -1){
|
||||
currentName = line.replace('#EXTINF:-1,','').trim()
|
||||
}else{
|
||||
parsedList[currentName] = line.trim()
|
||||
}
|
||||
})
|
||||
$.each(parsedList,function(name,url){
|
||||
var link = getUrlPieces(url)
|
||||
var newMon = $.aM.generateDefaultMonitorSettings()
|
||||
newMon.details = JSON.parse(newMon.details)
|
||||
newMon.mid = 'HLS' + name.toLowerCase()
|
||||
newMon.name = name
|
||||
newMon.port = link.port
|
||||
newMon.host = link.hostname
|
||||
newMon.path = link.pathname
|
||||
newMon.details.tv_channel = '1'
|
||||
newMon.details.tv_channel_id = name
|
||||
newMon.details.auto_host_enable = '1'
|
||||
newMon.details.auto_host = url
|
||||
newMon.details.stream_quality = '1'
|
||||
newMon.details.stream_fps = ''
|
||||
newMon.details.stream_vcodec = 'libx264'
|
||||
newMon.details.stream_acodec = 'aac'
|
||||
newMon.details.stream_type = 'hls'
|
||||
newMon.details.hls_time = '10'
|
||||
newMon.type = 'mp4'
|
||||
newMon.details = JSON.stringify(newMon.details)
|
||||
postMonitor(newMon)
|
||||
})
|
||||
}else{
|
||||
$.ccio.log(err)
|
||||
$.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'})
|
||||
}
|
||||
}
|
||||
// });
|
||||
});
|
||||
|
@ -116,7 +167,7 @@ $.multimon.getSelectedMonitors = function(unclean){
|
|||
$.multimon.e.find('.delete').click(function(){
|
||||
var arr=$.multimon.getSelectedMonitors(true);
|
||||
if(arr.length===0){
|
||||
$.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'});
|
||||
$.ccio.init('note',{title:lang['No Monitors Selected'],text:lang['Select atleast one monitor to delete'],type:'error'});
|
||||
return
|
||||
}
|
||||
$.confirm.e.modal('show');
|
||||
|
@ -125,7 +176,7 @@ $.multimon.e.find('.delete').click(function(){
|
|||
$.confirm.body.html(e.html)
|
||||
$.confirm.click([
|
||||
{
|
||||
title:'Delete Monitors',
|
||||
title:lang['Delete']+' '+lang['Monitors'],
|
||||
class:'btn-danger',
|
||||
callback:function(){
|
||||
$.each(arr,function(n,v){
|
||||
|
@ -136,7 +187,7 @@ $.multimon.e.find('.delete').click(function(){
|
|||
}
|
||||
},
|
||||
{
|
||||
title:'Delete Monitors and Files',
|
||||
title:lang['Delete Monitors and Files'],
|
||||
class:'btn-danger',
|
||||
callback:function(){
|
||||
$.each(arr,function(n,v){
|
||||
|
@ -152,7 +203,7 @@ $.multimon.e.find('.delete').click(function(){
|
|||
// var arr=$.multimon.getSelectedMonitors();
|
||||
// var arrObject={}
|
||||
// if(arr.length===0){
|
||||
// $.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'});
|
||||
// $.ccio.init('note',{title:lang['No Monitors Selected'],text:lang['Select atleast one monitor to delete'],type:'error'});
|
||||
// return
|
||||
// }
|
||||
// $.multimonedit.selectedList = arr;
|
||||
|
@ -162,7 +213,7 @@ $.multimon.e.find('.save_config').click(function(){
|
|||
var e={};e.e=$(this);
|
||||
var arr=$.multimon.getSelectedMonitors();
|
||||
if(arr.length===0){
|
||||
$.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'});
|
||||
$.ccio.init('note',{title:lang['No Monitors Selected'],text:lang['Select atleast one monitor to delete'],type:'error'});
|
||||
return
|
||||
}
|
||||
e.dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(arr));
|
||||
|
|
616
web/libs/js/dash2.powerVideo2.js
Normal file
616
web/libs/js/dash2.powerVideo2.js
Normal file
|
@ -0,0 +1,616 @@
|
|||
$(document).ready(function(e){
|
||||
var powerVideoWindow = $('#powerVideo')
|
||||
var powerVideoMonitorsListElement = $('#powerVideoMonitorsList')
|
||||
var powerVideoMonitorViewsElement = $('#powerVideoMonitorViews')
|
||||
var powerVideoTimelineStripsContainer = $('#powerVideoTimelineStrips')
|
||||
var powerVideoDateRangeElement = $('#powerVideoDateRange')
|
||||
var powerVideoVideoLimitElement = $('#powerVideoVideoLimit')
|
||||
var powerVideoEventLimitElement = $('#powerVideoEventLimit')
|
||||
var powerVideoLoadedVideos = {}
|
||||
var powerVideoLoadedEvents = {}
|
||||
var powerVideoLoadedChartData = {}
|
||||
var loadedTableGroupIds = {}
|
||||
var eventsLabeledByTime = {}
|
||||
var monitorSlotPlaySpeeds = {}
|
||||
var currentlyPlayingVideos = {}
|
||||
var extenders = {
|
||||
onVideoPlayerTimeUpdateExtensions: [],
|
||||
onVideoPlayerTimeUpdate: function(extender){
|
||||
extenders.onVideoPlayerTimeUpdateExtensions.push(extender)
|
||||
},
|
||||
onVideoPlayerCreateExtensions: [],
|
||||
onVideoPlayerCreate: function(extender){
|
||||
extenders.onVideoPlayerCreateExtensions.push(extender)
|
||||
},
|
||||
}
|
||||
var activeTimeline = null
|
||||
// fix utc/localtime translation (use timelapseJpeg as guide, it works as expected) >
|
||||
powerVideoDateRangeElement.daterangepicker({
|
||||
startDate:$.ccio.timeObject().subtract(moment.duration("24:00:00")),
|
||||
endDate:$.ccio.timeObject().add(moment.duration("24:00:00")),
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
timePickerSeconds: true,
|
||||
timePickerIncrement: 30,
|
||||
locale: {
|
||||
format: 'MM/DD/YYYY h:mm A'
|
||||
}
|
||||
},function(start, end, label){
|
||||
// $.pwrvid.drawTimeline()
|
||||
powerVideoDateRangeElement.focus()
|
||||
getSelectedMonitors().each(function(n,activeElement){
|
||||
var monitorId = $(activeElement).attr('data-monitor')
|
||||
requestTableData(monitorId)
|
||||
})
|
||||
});
|
||||
// fix utc/localtime translation (use timelapseJpeg as guide, it works as expected) />
|
||||
var loadVideosToTimeLineMemory = function(monitorId,videos,events){
|
||||
powerVideoLoadedVideos[monitorId] = videos
|
||||
powerVideoLoadedEvents[monitorId] = events
|
||||
}
|
||||
var drawMonitorsList = function(){
|
||||
var html = ''
|
||||
$.each($.ccio.mon,function(n,monitor){
|
||||
html += `<div class="list-group-item" data-monitor="${monitor.mid}">${monitor.name}</div>`
|
||||
})
|
||||
powerVideoMonitorsListElement.html(html)
|
||||
}
|
||||
var requestTableData = function(monitorId,user){
|
||||
if(!user)user = $user
|
||||
var dateData = powerVideoDateRangeElement.data('daterangepicker')
|
||||
$.ccio.cx({
|
||||
f: 'monitor',
|
||||
ff: 'get',
|
||||
fff: 'videos&events',
|
||||
videoLimit: parseInt(powerVideoVideoLimitElement.val()) || 0,
|
||||
eventLimit: parseInt(powerVideoEventLimitElement.val()) || 500,
|
||||
startDate: dateData.startDate.utc().format('YYYY-MM-DDTHH:mm:ss'),
|
||||
endDate: dateData.endDate.utc().format('YYYY-MM-DDTHH:mm:ss'),
|
||||
ke: user.ke,
|
||||
mid: monitorId
|
||||
})
|
||||
}
|
||||
var unloadTableData = function(monitorId,user){
|
||||
if(!user)user = $user
|
||||
delete(powerVideoLoadedVideos[monitorId])
|
||||
delete(powerVideoLoadedEvents[monitorId])
|
||||
delete(loadedTableGroupIds[monitorId])
|
||||
delete(loadedTableGroupIds[monitorId + '_events'])
|
||||
powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid="${monitorId}"]`).remove()
|
||||
drawLoadedTableData()
|
||||
}
|
||||
var checkEventsAgainstVideo = function(video,events){
|
||||
var videoStartTime = new Date(video.time)
|
||||
var videoEndTime = new Date(video.end)
|
||||
var eventsToCheck = events
|
||||
video.detections = {}
|
||||
var newSetOfEventsWithoutChecked = {}
|
||||
$.each(eventsToCheck,function(n,event){
|
||||
var eventTime = new Date(event.time)
|
||||
var seekPosition = (eventTime - videoStartTime) / 1000
|
||||
if (videoStartTime <= eventTime && eventTime <= videoEndTime) {
|
||||
if(!video.details.confidence)video.details.confidence = 0
|
||||
video.detections[seekPosition] = event
|
||||
eventsLabeledByTime[video.mid][video.time][seekPosition] = event
|
||||
}else{
|
||||
newSetOfEventsWithoutChecked[n] = video
|
||||
}
|
||||
})
|
||||
eventsToCheck = newSetOfEventsWithoutChecked
|
||||
}
|
||||
var prepareVideosAndEventsForTable = function(monitorId,videos,events){
|
||||
var chartData = []
|
||||
eventsLabeledByTime[monitorId] = {}
|
||||
$.each(videos,function(n,video){
|
||||
eventsLabeledByTime[monitorId][video.time] = {}
|
||||
if(videos[n - 1])video.videoAfter = videos[n - 1]
|
||||
if(videos[n + 1])video.videoBefore = videos[n + 1]
|
||||
checkEventsAgainstVideo(video,events)
|
||||
chartData.push({
|
||||
group: loadedTableGroupIds[monitorId],
|
||||
content: `<div timeline-video-file="${video.mid}${video.time}">
|
||||
${video.time}
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" style="width:0%;"><span></span></div>
|
||||
</div>
|
||||
</div>`,
|
||||
start: video.time,
|
||||
end: video.end,
|
||||
videoInfo: video
|
||||
})
|
||||
})
|
||||
$.each(events,function(n,event){
|
||||
var eventReason = event.details.reason.toUpperCase()
|
||||
var eventSlotTag = eventReason
|
||||
if(eventReason === 'OBJECT' && event.details.matrices[0]){
|
||||
eventSlotTag = []
|
||||
event.details.matrices.forEach(function(matrix){
|
||||
eventSlotTag.push(matrix.tag)
|
||||
})
|
||||
eventSlotTag = eventSlotTag.join(', ')
|
||||
}
|
||||
chartData.push({
|
||||
group: loadedTableGroupIds[monitorId + '_events'],
|
||||
content: `<div timeline-event="${event.time}">${eventSlotTag}</div>`,
|
||||
start: event.time,
|
||||
eventInfo: event
|
||||
})
|
||||
})
|
||||
return chartData
|
||||
}
|
||||
var getAllChartDataForLoadedVideos = function(){
|
||||
var chartData = []
|
||||
Object.keys(powerVideoLoadedVideos).forEach(function(monitorId,n){
|
||||
var videos = powerVideoLoadedVideos[monitorId]
|
||||
var events = powerVideoLoadedEvents[monitorId]
|
||||
var parsedVideos = prepareVideosAndEventsForTable(monitorId,videos,events)
|
||||
powerVideoLoadedChartData[monitorId] = parsedVideos
|
||||
chartData = chartData.concat(parsedVideos)
|
||||
})
|
||||
return chartData
|
||||
}
|
||||
var visuallySelectItemInRow = function(video){
|
||||
powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"]`).parents('.vis-item').addClass('vis-selected')
|
||||
}
|
||||
var visuallyDeselectItemInRow = function(video){
|
||||
powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"]`).parents('.vis-item').removeClass('vis-selected')
|
||||
}
|
||||
var drawTableTimeout = null
|
||||
var drawLoadedTableData = function(){
|
||||
// destroy old
|
||||
try{
|
||||
if(activeTimeline && activeTimeline.destroy){
|
||||
activeTimeline.destroy()
|
||||
}
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
//
|
||||
powerVideoTimelineStripsContainer.html(`<div class="loading"><i class="fa fa-spinner fa-pulse"></i><div class="epic-text">${lang['Please Wait...']}</div></div>`)
|
||||
clearTimeout(drawTableTimeout)
|
||||
drawTableTimeout = setTimeout(function(){
|
||||
var container = powerVideoTimelineStripsContainer[0]
|
||||
var groupsDataSet = new vis.DataSet()
|
||||
var groups = []
|
||||
var groupId = 1
|
||||
Object.keys(powerVideoLoadedVideos).forEach(function(monitorId,n){
|
||||
groups.push({
|
||||
id: groupId,
|
||||
content: monitorId
|
||||
})
|
||||
groupId += 1
|
||||
groups.push({
|
||||
id: groupId,
|
||||
content: lang.Events
|
||||
})
|
||||
groupId += 1
|
||||
loadedTableGroupIds[monitorId] = groupId - 2
|
||||
loadedTableGroupIds[monitorId + '_events'] = groupId - 1
|
||||
})
|
||||
groupsDataSet.add(groups)
|
||||
var chartData = getAllChartDataForLoadedVideos()
|
||||
if(chartData.length > 0){
|
||||
var items = new vis.DataSet(chartData)
|
||||
var options = {
|
||||
selectable: false,
|
||||
stack: false,
|
||||
showCurrentTime: false,
|
||||
}
|
||||
// Create a Timeline
|
||||
var timeline = new vis.Timeline(container, items, groupsDataSet, options)
|
||||
powerVideoTimelineStripsContainer.find('.loading').remove()
|
||||
var timeChanging = false
|
||||
timeline.on('rangechange', function(properties){
|
||||
timeChanging = true
|
||||
})
|
||||
timeline.on('rangechanged', function(properties){
|
||||
setTimeout(function(){
|
||||
timeChanging = false
|
||||
},300)
|
||||
})
|
||||
timeline.on('click', function(properties){
|
||||
if(!timeChanging){
|
||||
var selectedTime = properties.time
|
||||
var videosAtSameTime = findAllVideosAtTime(selectedTime)
|
||||
powerVideoTimelineStripsContainer.find('.vis-item').removeClass('vis-selected')
|
||||
$.each(videosAtSameTime,function(monitorId,videos){
|
||||
var selectedVideo = videos[0]
|
||||
if(selectedVideo){
|
||||
loadVideoIntoMonitorSlot(selectedVideo,selectedTime)
|
||||
visuallySelectItemInRow(selectedVideo)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
activeTimeline = timeline
|
||||
}else{
|
||||
powerVideoTimelineStripsContainer.html(`<div class="loading"><i class="fa fa-exclamation-circle"></i><div class="epic-text">${lang['No Data']}</div></div>`)
|
||||
}
|
||||
},1000)
|
||||
}
|
||||
var drawMatrices = function(event,options){
|
||||
var streamObjectsContainer = options.streamObjectsContainer
|
||||
var height = options.height
|
||||
var width = options.width
|
||||
var monitorId = options.mid
|
||||
var widthRatio = width / event.details.imgWidth
|
||||
var heightRatio = height / event.details.imgHeight
|
||||
|
||||
streamObjectsContainer.find('.stream-detected-object[name="'+event.details.name+'"]').remove()
|
||||
var html = ''
|
||||
$.each(event.details.matrices,function(n,matrix){
|
||||
html += `<div class="stream-detected-object" name="${event.details.name}" style="height:${heightRatio * matrix.height}px;width:${widthRatio * matrix.width}px;top:${heightRatio * matrix.y}px;left:${widthRatio * matrix.x}px;">`
|
||||
if(matrix.tag)html += `<span class="tag">${matrix.tag}</span>`
|
||||
html += '</div>'
|
||||
})
|
||||
streamObjectsContainer.append(html)
|
||||
}
|
||||
var attachEventsToVideoActiveElement = function(video){
|
||||
var monitorId = video.mid
|
||||
var videoPlayerContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}]`)
|
||||
var videoElement = videoPlayerContainer.find(`video.videoNow`)
|
||||
var streamObjectsContainer = videoPlayerContainer.find(`.videoPlayer-stream-objects`)
|
||||
var detectionInfoContainerMotion = videoPlayerContainer.find(`.videoPlayer-detection-info-motion`)
|
||||
var detectionInfoContainerObject = videoPlayerContainer.find(`.videoPlayer-detection-info-object`)
|
||||
var detectionInfoContainerRaw = videoPlayerContainer.find(`.videoPlayer-detection-info-raw`)
|
||||
var motionMeterProgressBar = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar`)
|
||||
var motionMeterProgressBarTextBox = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar span`)
|
||||
var videoCurrentTimeProgressBar = powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"] .progress-bar`)[0]
|
||||
var preloadedNext = false
|
||||
var reinitializeStreamObjectsContainer = function(){
|
||||
height = videoElement.height()
|
||||
width = videoElement.width()
|
||||
}
|
||||
reinitializeStreamObjectsContainer()
|
||||
$(videoElement)
|
||||
.resize(reinitializeStreamObjectsContainer)
|
||||
// .off('loadeddata').on('loadeddata', function() {
|
||||
// reinitializeStreamObjectsContainer()
|
||||
// var allLoaded = true
|
||||
// getAllActiveVideosInSlots().each(function(n,videoElement){
|
||||
// if(!videoElement.readyState === 4)allLoaded = false
|
||||
// })
|
||||
// setTimeout(function(){
|
||||
// if(allLoaded){
|
||||
// playAllSlots()
|
||||
// }
|
||||
// },1500)
|
||||
// })
|
||||
// .off("pause").on("pause",function(){
|
||||
// console.log(monitorId,'pause')
|
||||
// })
|
||||
// .off("play").on("play",function(){
|
||||
// console.log(monitorId,'play')
|
||||
// })
|
||||
.off("timeupdate").on("timeupdate",function(){
|
||||
var event = eventsLabeledByTime[monitorId][video.time][parseInt(this.currentTime)]
|
||||
if(event){
|
||||
if(event.details.matrices){
|
||||
drawMatrices(event,{
|
||||
streamObjectsContainer: streamObjectsContainer,
|
||||
monitorId: monitorId,
|
||||
height: height,
|
||||
width: width,
|
||||
})
|
||||
detectionInfoContainerObject.html($.ccio.init('jsontoblock',event.details.matrices))
|
||||
}
|
||||
if(event.details.confidence){
|
||||
motionMeterProgressBar.css('width',event.details.confidence+'px')
|
||||
motionMeterProgressBarTextBox.text(event.details.confidence)
|
||||
var html = `<div>${lang['Region']} : ${event.details.name}</div>
|
||||
<div>${lang['Confidence']} : ${event.details.confidence}</div>
|
||||
<div>${lang['Plugin']} : ${event.details.plug}</div>`
|
||||
detectionInfoContainerMotion.html(html)
|
||||
// detectionInfoContainerRaw.html($.ccio.init('jsontoblock',{`${lang['Plug']}`:event.details.plug}))
|
||||
}
|
||||
}
|
||||
var currentTime = this.currentTime;
|
||||
var watchPoint = Math.floor((currentTime/this.duration) * 100)
|
||||
if(!preloadedNext && watchPoint >= 75){
|
||||
preloadedNext = true
|
||||
var videoAfter = videoPlayerContainer.find(`video.videoAfter`)[0]
|
||||
videoAfter.setAttribute('preload',true)
|
||||
}
|
||||
if(videoCurrentTimeProgressBar)videoCurrentTimeProgressBar.style.width = `${watchPoint}px`
|
||||
extenders.onVideoPlayerTimeUpdateExtensions.forEach(function(extender){
|
||||
extender(videoElement,watchPoint)
|
||||
})
|
||||
})
|
||||
var onEnded = function() {
|
||||
visuallyDeselectItemInRow(video)
|
||||
if(video.videoAfter){
|
||||
visuallySelectItemInRow(video.videoAfter)
|
||||
loadVideoIntoMonitorSlot(video.videoAfter)
|
||||
}
|
||||
}
|
||||
videoElement[0].onended = onEnded
|
||||
videoElement[0].onerror = onEnded
|
||||
}
|
||||
var dettachEventsToVideoActiveElement = function(monitorId){
|
||||
var videoElement = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}] video.videoNow`)
|
||||
$(videoElement)
|
||||
// .off('loadeddata')
|
||||
.off("pause")
|
||||
.off("play")
|
||||
.off("timeupdate")
|
||||
}
|
||||
var findAllVideosAtTime = function(selectedTime){
|
||||
var time = new Date(selectedTime)
|
||||
var parsedVideos = {}
|
||||
$.each(powerVideoLoadedVideos,function(monitorId,videos){
|
||||
var videosFilteredByTime = videos.filter(function(video){
|
||||
return (
|
||||
(new Date(video.time)) <= time && time < (new Date(video.end))
|
||||
)
|
||||
});
|
||||
parsedVideos[monitorId] = videosFilteredByTime
|
||||
})
|
||||
return parsedVideos
|
||||
}
|
||||
var resetVisualDetectionDataForMonitorSlot = function(monitorId){
|
||||
var videoPlayerContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}]`)
|
||||
var streamObjectsContainer = videoPlayerContainer.find(`.videoPlayer-stream-objects`)
|
||||
var detectionInfoContainerObject = videoPlayerContainer.find(`.videoPlayer-detection-info-object`)
|
||||
var detectionInfoContainerMotion = videoPlayerContainer.find(`.videoPlayer-detection-info-motion`)
|
||||
var motionMeterProgressBar = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar`)
|
||||
var motionMeterProgressBarTextBox = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar span`)
|
||||
detectionInfoContainerObject.empty()
|
||||
detectionInfoContainerMotion.empty()
|
||||
streamObjectsContainer.empty()
|
||||
motionMeterProgressBar.css('width','0')
|
||||
motionMeterProgressBarTextBox.text('0')
|
||||
}
|
||||
var loadVideoIntoMonitorSlot = function(video,selectedTime){
|
||||
if(!video)return
|
||||
resetVisualDetectionDataForMonitorSlot(video.mid)
|
||||
currentlyPlayingVideos[video.mid] = video
|
||||
var timeToStartAt = selectedTime - new Date(video.time)
|
||||
var numberOfMonitors = Object.keys(powerVideoLoadedVideos).length
|
||||
// if(numberOfMonitors > 3)numberOfMonitors = 3 //start new row after 3
|
||||
if(numberOfMonitors == 1)numberOfMonitors = 2 //make single monitor not look like a doofus
|
||||
if(timeToStartAt < 0)timeToStartAt = 0
|
||||
var widthOfBlock = 100 / numberOfMonitors
|
||||
var videoContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${video.mid}] .videoPlayer-buffers`)
|
||||
if(videoContainer.length === 0){
|
||||
if(!monitorSlotPlaySpeeds)monitorSlotPlaySpeeds[video.mid] = {}
|
||||
powerVideoMonitorViewsElement.append(`<div class="videoPlayer" style="width:${widthOfBlock}%" data-mid="${video.mid}">
|
||||
<div class="videoPlayer-detection-info">
|
||||
<div class="videoPlayer-detection-info-raw"></div>
|
||||
<h5 class="epic-text">${lang.Motion}</h5>
|
||||
<div class="videoPlayer-detection-info-motion"></div>
|
||||
<h5 class="epic-text">${lang.Matrices}</h5>
|
||||
<div class="videoPlayer-detection-info-object"></div>
|
||||
</div>
|
||||
<div class="videoPlayer-stream-objects"></div>
|
||||
<div class="videoPlayer-buffers"></div>
|
||||
<div class="videoPlayer-motion-meter progress" title="${lang['Motion Meter']}">
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" style="width:0%;"><span></span></div>
|
||||
</div>
|
||||
</div>`)
|
||||
videoContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${video.mid}] .videoPlayer-buffers`)
|
||||
}else{
|
||||
powerVideoMonitorViewsElement.find('.videoPlayer').css('width',`${widthOfBlock}%`)
|
||||
}
|
||||
var videoCurrentNow = videoContainer.find('.videoNow')
|
||||
var videoCurrentAfter = videoContainer.find('.videoAfter')
|
||||
// var videoCurrentBefore = videoContainer.find('.videoBefore')
|
||||
dettachEventsToVideoActiveElement(video.mid)
|
||||
videoContainer.find('video').each(function(n,v){
|
||||
v.pause()
|
||||
})
|
||||
var videoIsSame = (video.href == videoCurrentNow.attr('video'))
|
||||
var videoIsAfter = (video.href == videoCurrentAfter.attr('video'))
|
||||
// var videoIsBefore = (video.href == videoCurrentBefore.attr('video'))
|
||||
var drawVideoHTML = function(position){
|
||||
var videoData
|
||||
var exisitingElement = videoContainer.find('.' + position)
|
||||
if(position){
|
||||
videoData = video[position]
|
||||
}else{
|
||||
position = 'videoNow'
|
||||
videoData = video
|
||||
}
|
||||
if(videoData){
|
||||
videoContainer.append('<video class="video_video '+position+'" video="'+videoData.href+'" playsinline><source src="'+videoData.href+'" type="video/'+videoData.ext+'"></video>')
|
||||
}
|
||||
}
|
||||
if(
|
||||
videoIsSame ||
|
||||
videoIsAfter
|
||||
// || videoIsBefore
|
||||
){
|
||||
switch(true){
|
||||
case videoIsSame:
|
||||
var videoNow = videoContainer.find('video.videoNow')[0]
|
||||
if(!videoNow.paused)videoNow.pause()
|
||||
videoNow.currentTime = timeToStartAt / 1000
|
||||
if(videoNow.paused)videoNow.play()
|
||||
return
|
||||
break;
|
||||
case videoIsAfter:
|
||||
// videoCurrentBefore.remove()
|
||||
videoCurrentNow.remove()
|
||||
videoCurrentAfter.removeClass('videoAfter').addClass('videoNow')
|
||||
// videoCurrentNow.removeClass('videoNow').addClass('videoBefore')
|
||||
drawVideoHTML('videoAfter')
|
||||
break;
|
||||
// case videoIsBefore:
|
||||
// videoCurrentAfter.remove()
|
||||
// videoCurrentBefore.removeClass('videoBefore').addClass('videoNow')
|
||||
// videoCurrentNow.removeClass('videoNow').addClass('videoAfter')
|
||||
// drawVideoHTML('videoBefore')
|
||||
// break;
|
||||
}
|
||||
}else{
|
||||
videoContainer.empty()
|
||||
drawVideoHTML()//videoNow
|
||||
// drawVideoHTML('videoBefore')
|
||||
drawVideoHTML('videoAfter')
|
||||
}
|
||||
var videoNow = videoContainer.find('video.videoNow')[0]
|
||||
attachEventsToVideoActiveElement(video)
|
||||
//
|
||||
videoNow.setAttribute('preload',true)
|
||||
videoNow.muted = true
|
||||
videoNow.playbackRate = monitorSlotPlaySpeeds[video.mid] || 1
|
||||
videoNow.currentTime = timeToStartAt / 1000
|
||||
videoNow.play()
|
||||
extenders.onVideoPlayerCreateExtensions.forEach(function(extender){
|
||||
extender(videoElement,watchPoint)
|
||||
})
|
||||
}
|
||||
var getSelectedMonitors = function(){
|
||||
return powerVideoMonitorsListElement.find('.active')
|
||||
}
|
||||
var getAllActiveVideosInSlots = function(){
|
||||
return powerVideoMonitorViewsElement.find('video.videoNow')
|
||||
}
|
||||
var pauseAllSlots = function(){
|
||||
getAllActiveVideosInSlots().each(function(n,video){
|
||||
if(!video.paused)video.pause()
|
||||
})
|
||||
}
|
||||
var toggleZoomAllSlots = function(){
|
||||
powerVideoMonitorViewsElement.find(`.videoPlayer`).each(function(n,videoContainer){
|
||||
var streamWindow = $(videoContainer)
|
||||
var monitorId = streamWindow.attr('data-mid')
|
||||
var enabled = streamWindow.attr('zoomEnabled')
|
||||
if(enabled === '1'){
|
||||
streamWindow
|
||||
.attr('zoomEnabled','0')
|
||||
.off('mouseover')
|
||||
.off('mouseout')
|
||||
.off('mousemove')
|
||||
.off('touchmove')
|
||||
.find('.zoomGlass').remove()
|
||||
}else{
|
||||
const magnifyStream = function(e){
|
||||
var videoElement = streamWindow.find('video.videoNow')
|
||||
console.log(videoElement[0].currentTime)
|
||||
$.ccio.magnifyStream({
|
||||
p: streamWindow,
|
||||
videoUrl: streamWindow.find('video.videoNow').find('source').attr('src'),
|
||||
setTime: videoElement[0].currentTime,
|
||||
monitor: $.ccio.mon[$user.ke + monitorId + $user.auth_token],
|
||||
targetForZoom: 'video.videoNow',
|
||||
magnifyOffsetElement: '.videoPlayer-buffers',
|
||||
zoomAmount: 1,
|
||||
auto: false,
|
||||
animate: false,
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY
|
||||
},$user)
|
||||
}
|
||||
streamWindow
|
||||
.attr('zoomEnabled','1')
|
||||
.on('mouseover', function(){
|
||||
streamWindow.find(".zoomGlass").show()
|
||||
})
|
||||
.on('mouseout', function(){
|
||||
streamWindow.find(".zoomGlass").hide()
|
||||
})
|
||||
.on('mousemove', magnifyStream)
|
||||
.on('touchmove', magnifyStream)
|
||||
}
|
||||
})
|
||||
}
|
||||
var playAllSlots = function(){
|
||||
getAllActiveVideosInSlots().each(function(n,video){
|
||||
if(video.paused)video.play()
|
||||
})
|
||||
}
|
||||
var setPlaySpeedOnAllSlots = function(playSpeed){
|
||||
Object.keys(powerVideoLoadedVideos).forEach(function(monitorId){
|
||||
monitorSlotPlaySpeeds[monitorId] = playSpeed
|
||||
})
|
||||
getAllActiveVideosInSlots().each(function(n,video){
|
||||
video.playbackRate = playSpeed
|
||||
})
|
||||
}
|
||||
var nextVideoAllSlots = function(){
|
||||
Object.keys(currentlyPlayingVideos).forEach(function(monitorId){
|
||||
var video = currentlyPlayingVideos[monitorId]
|
||||
visuallyDeselectItemInRow(video)
|
||||
visuallySelectItemInRow(video.videoAfter)
|
||||
loadVideoIntoMonitorSlot(video.videoAfter,0)
|
||||
})
|
||||
}
|
||||
var previousVideoAllSlots = function(){
|
||||
Object.keys(currentlyPlayingVideos).forEach(function(monitorId){
|
||||
var video = currentlyPlayingVideos[monitorId]
|
||||
visuallyDeselectItemInRow(video)
|
||||
visuallySelectItemInRow(video.videoBefore)
|
||||
loadVideoIntoMonitorSlot(video.videoBefore,0)
|
||||
})
|
||||
}
|
||||
$user.ws.on('f',function (d){
|
||||
switch(d.f){
|
||||
case'videos&events':
|
||||
var videos = d.videos.videos
|
||||
var events = d.events
|
||||
loadVideosToTimeLineMemory(d.id,videos,events)
|
||||
drawLoadedTableData()
|
||||
break;
|
||||
}
|
||||
})
|
||||
$('body')
|
||||
.on('dblclick','.videoPlayer',function(){
|
||||
var el = $(this)
|
||||
$.ccio.init('fullscreen',this)
|
||||
})
|
||||
.on('click','[data-monitor]',function(){
|
||||
var el = $(this)
|
||||
var monitorId = el.attr('data-monitor')
|
||||
el.toggleClass('active')
|
||||
if(el.hasClass('active')){
|
||||
requestTableData(monitorId)
|
||||
}else{
|
||||
unloadTableData(monitorId)
|
||||
}
|
||||
})
|
||||
.on('click','[powerVideo-control]',function(){
|
||||
var el = $(this)
|
||||
var controlType = el.attr('powerVideo-control')
|
||||
switch(controlType){
|
||||
case'toggleZoom':
|
||||
toggleZoomAllSlots()
|
||||
break;
|
||||
case'playAll':
|
||||
playAllSlots()
|
||||
break;
|
||||
case'pauseAll':
|
||||
pauseAllSlots()
|
||||
break;
|
||||
case'playSpeedAll':
|
||||
var playSpeed = el.attr('data-speed')
|
||||
setPlaySpeedOnAllSlots(playSpeed)
|
||||
break;
|
||||
case'previousVideoAll':
|
||||
playAllSlots()
|
||||
previousVideoAllSlots()
|
||||
break;
|
||||
case'nextVideoAll':
|
||||
playAllSlots()
|
||||
nextVideoAllSlots()
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
powerVideoWindow.on('shown.bs.modal',function(){
|
||||
drawMonitorsList()
|
||||
})
|
||||
$.powerVideoViewer = {
|
||||
window: powerVideoWindow,
|
||||
drawMonitorsList: drawMonitorsList,
|
||||
activeTimeline: activeTimeline,
|
||||
monitorListElement: powerVideoMonitorsListElement,
|
||||
monitorViewsElement: powerVideoMonitorViewsElement,
|
||||
timelineStripsElement: powerVideoTimelineStripsContainer,
|
||||
dateRangeElement: powerVideoDateRangeElement,
|
||||
loadedVideos: powerVideoLoadedVideos,
|
||||
loadedEvents: powerVideoLoadedEvents,
|
||||
loadedChartData: powerVideoLoadedChartData,
|
||||
loadedTableGroupIds: loadedTableGroupIds,
|
||||
extenders: extenders
|
||||
}
|
||||
})
|
128
web/libs/js/dash2.schedules.js
Normal file
128
web/libs/js/dash2.schedules.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
$(document).ready(function(){
|
||||
$.schedules = {
|
||||
e: $('#schedules'),
|
||||
selector: $('#schedulesSelector'),
|
||||
loadedMonitorStates: {},
|
||||
loadedSchedules: {}
|
||||
}
|
||||
$.schedules.f = $.schedules.e.find('form')
|
||||
$.schedules.selectedStates = $.schedules.e.find('[name="monitorStates"]')
|
||||
$.schedules.selectedDays = $.schedules.e.find('[name="days"]')
|
||||
$.schedules.loadSchedules = function(callback){
|
||||
$.get($.ccio.init('location',$user) + $user.auth_token + '/schedule/' + $user.ke,function(d){
|
||||
console.log(d)
|
||||
var html = ''
|
||||
$.each(d.schedules,function(n,v){
|
||||
$.schedules.loadedSchedules[v.name] = v
|
||||
html += $.ccio.tm('option',{
|
||||
id: v.name,
|
||||
name: v.name
|
||||
})
|
||||
})
|
||||
$.schedules.selector.find('optgroup').html(html)
|
||||
if(callback)callback()
|
||||
})
|
||||
}
|
||||
$.schedules.loadMonitorStates = function(){
|
||||
$.get($.ccio.init('location',$user) + $user.auth_token + '/monitorStates/' + $user.ke,function(d){
|
||||
var html = ''
|
||||
$.each(d.presets,function(n,v){
|
||||
$.schedules.loadedMonitorStates[v.name] = v
|
||||
html += $.ccio.tm('option',{
|
||||
id: v.name,
|
||||
name: v.name
|
||||
})
|
||||
})
|
||||
$.schedules.selectedStates.html(html)
|
||||
})
|
||||
}
|
||||
$.schedules.e.on('shown.bs.modal', function (e) {
|
||||
$.schedules.loadMonitorStates()
|
||||
$.schedules.loadSchedules()
|
||||
})
|
||||
$.schedules.e.on('click','.delete',function(e){
|
||||
$.confirm.e.modal('show');
|
||||
$.confirm.title.text(lang['Delete Monitor States Preset']);
|
||||
$.confirm.body.html(lang.deleteMonitorStateText1);
|
||||
$.confirm.click({title:'Delete',class:'btn-danger'},function(){
|
||||
var form = $.schedules.f.serializeObject()
|
||||
$.post($.ccio.init('location',$user) + $user.auth_token + '/schedule/' + $user.ke + '/' + form.name + '/delete',function(d){
|
||||
$.ccio.log(d)
|
||||
if(d.ok === true){
|
||||
$.schedules.loadSchedules()
|
||||
$.ccio.init('note',{title:lang.Success,text:d.msg,type:'success'})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
$.schedules.selector.change(function(e){
|
||||
var selected = $(this).val()
|
||||
var loaded = $.schedules.loadedSchedules[selected]
|
||||
var namespace = $.schedules.e.find('[name="name"]')
|
||||
var deleteButton = $.schedules.e.find('.delete')
|
||||
var tzEl = $.schedules.e.find('[name="timezone"]')
|
||||
$.schedules.selectedStates.find('option:selected').removeAttr('selected')
|
||||
$.schedules.selectedDays.find('option:selected').removeAttr('selected')
|
||||
if(loaded){
|
||||
namespace.val(loaded.name)
|
||||
var html = ''
|
||||
$.each(loaded,function(n,v){
|
||||
$.schedules.f.find('[name="' + n + '"]').val(v)
|
||||
})
|
||||
$.each(loaded.details.monitorStates,function(n,v){
|
||||
$.schedules.selectedStates.find('option[value="' + v + '"]').prop('selected',true)
|
||||
})
|
||||
$.each(loaded.details.days,function(n,v){
|
||||
$.schedules.selectedDays.find('option[value="' + v + '"]').prop('selected',true)
|
||||
})
|
||||
tzEl.val(loaded.details.timezone || '+0')
|
||||
deleteButton.show()
|
||||
}else{
|
||||
tzEl.val('+0')
|
||||
namespace.val('')
|
||||
deleteButton.hide()
|
||||
}
|
||||
})
|
||||
$.schedules.f.submit(function(e){
|
||||
e.preventDefault()
|
||||
var el = $(this)
|
||||
var form = el.serializeObject()
|
||||
var monitors = []
|
||||
var failedToParseAJson = false
|
||||
var rows = $.monitorStates.monitors.find('.state-monitor-row')
|
||||
if(form.name === ''){
|
||||
return $.ccio.init('note',{title:lang['Invalid Data'],text:lang['Name cannot be empty.'],type:'error'})
|
||||
}
|
||||
if(form.start === ''){
|
||||
return $.ccio.init('note',{title:lang['Invalid Data'],text:lang['Start Time cannot be empty.'],type:'error'})
|
||||
}
|
||||
if(form.monitorStates instanceof Array === false){
|
||||
form.monitorStates = [form.monitorStates]
|
||||
}
|
||||
if(!form.days || form.days === ''){
|
||||
form.days = null
|
||||
}else if(form.days instanceof Array === false){
|
||||
form.days = [form.days]
|
||||
}
|
||||
var data = {
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
enabled: form.enabled,
|
||||
details: {
|
||||
monitorStates: form.monitorStates,
|
||||
days: form.days,
|
||||
timezone: form.timezone,
|
||||
}
|
||||
}
|
||||
$.post($.ccio.init('location',$user) + $user.auth_token + '/schedule/' + $user.ke + '/' + form.name + '/insert',{data:data},function(d){
|
||||
$.ccio.log(d)
|
||||
if(d.ok === true){
|
||||
$.schedules.loadSchedules(function(){
|
||||
$.schedules.selector.val(form.name)
|
||||
})
|
||||
$.ccio.init('note',{title:lang.Success,text:d.msg,type:'success'})
|
||||
}
|
||||
})
|
||||
return false;
|
||||
})
|
||||
})
|
|
@ -7,6 +7,13 @@ $.ccio.cx=function(x,user){
|
|||
if(!x.uid){x.uid=user.uid;};
|
||||
return user.ws.emit('f',x)
|
||||
}
|
||||
$.diskUsed = {
|
||||
main: $('.diskUsed'),
|
||||
list: {},
|
||||
}
|
||||
$.each(addStorage,function(n,storage){
|
||||
$.diskUsed.list[storage.name] = $(`#diskUsedList [storage="${storage.name}"]`)
|
||||
})
|
||||
$.ccio.globalWebsocket=function(d,user){
|
||||
if(d.f!=='monitor_frame'&&d.f!=='os'&&d.f!=='video_delete'&&d.f!=='detector_trigger'&&d.f!=='detector_record_timeout_start'&&d.f!=='log'){$.ccio.log(d);}
|
||||
if(!user){
|
||||
|
@ -151,12 +158,26 @@ $.ccio.globalWebsocket=function(d,user){
|
|||
break;
|
||||
case'diskUsed':
|
||||
if(!d.limit||d.limit===''){d.limit=10000}
|
||||
d.percent=parseInt((d.size/d.limit)*100)+'%';
|
||||
d.human=parseFloat(d.size)
|
||||
d.percent = parseInt((d.size/d.limit)*100)+'%';
|
||||
d.human = parseFloat(d.size)
|
||||
if(d.human>1000){d.human=(d.human/1000).toFixed(2)+' GB'}else{d.human=d.human.toFixed(2)+' MB'}
|
||||
$('.diskUsed .value').html(d.human)
|
||||
$('.diskUsed .percent').html(d.percent)
|
||||
$('.diskUsed .progress-bar').css('width',d.percent)
|
||||
$.diskUsed.main.find('.value').html(d.human)
|
||||
$.diskUsed.main.find('.percent').html(d.percent)
|
||||
$.diskUsed.main.find('.progress-bar').css('width',d.percent)
|
||||
if(d.addStorage){
|
||||
$.each(d.addStorage,function(n,storage){
|
||||
var percent = parseInt((storage.usedSpace/storage.sizeLimit)*100)+'%'
|
||||
var humanValue = parseFloat(storage.usedSpace)
|
||||
if(humanValue > 1000){
|
||||
humanValue = (humanValue/1000).toFixed(2)+' GB'
|
||||
}else{
|
||||
humanValue = humanValue.toFixed(2)+' MB'
|
||||
}
|
||||
$.diskUsed.list[storage.name].find('.value').html(humanValue)
|
||||
$.diskUsed.list[storage.name].find('.percent').html(percent)
|
||||
$.diskUsed.list[storage.name].find('.progress-bar').css('width',percent)
|
||||
})
|
||||
}
|
||||
break;
|
||||
case'video_fix_success':case'video_fix_start':
|
||||
switch(d.f){
|
||||
|
@ -723,9 +744,14 @@ $.ccio.globalWebsocket=function(d,user){
|
|||
type: "time",
|
||||
display: true,
|
||||
time: {
|
||||
format: timeFormat,
|
||||
// round: 'day'
|
||||
}
|
||||
// format: timeFormat,
|
||||
unit: 'minute',
|
||||
displayFormats: {
|
||||
minute: 'h:mm a',
|
||||
},
|
||||
},
|
||||
categoryPercentage: 0.6,
|
||||
barPercentage: .5,
|
||||
}],
|
||||
},
|
||||
}
|
||||
|
|
14
web/libs/js/dash2.tabs.js
Normal file
14
web/libs/js/dash2.tabs.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
$(document).ready(function(){
|
||||
$('body')
|
||||
.on('click','[tab-chooser]',function(){
|
||||
var el = $(this)
|
||||
var parent = el.parents('[tab-chooser-parent]')
|
||||
var tabName = el.attr('[tab-chooser]')
|
||||
var allTabChoosersInParent = parent.find('[tab-chooser]')
|
||||
var allTabsInParent = parent.find('[tab-section]')
|
||||
allTabsInParent.hide()
|
||||
allTabChoosersInParent.removeClass('active')
|
||||
el.addClass('active')
|
||||
parent.find(`[tab-section="${tabName}"]`).show()
|
||||
})
|
||||
})
|
174
web/libs/js/dash2.timelapse.jpeg.js
Normal file
174
web/libs/js/dash2.timelapse.jpeg.js
Normal file
|
@ -0,0 +1,174 @@
|
|||
$(document).ready(function(e){
|
||||
//Timelapse JPEG Window
|
||||
$.timelapseJpeg = {e:$('#timelapsejpeg')}
|
||||
$.timelapseJpeg.datepicker = $('#timelapsejpeg_date')
|
||||
$.timelapseJpeg.timelapseJpegFps = $('#timelapseJpegFps')
|
||||
$.timelapseJpeg.framesContainer = $.timelapseJpeg.e.find('.frames')
|
||||
$.timelapseJpeg.frameStrip = $.timelapseJpeg.e.find('.frameStrip')
|
||||
$.timelapseJpeg.frameIcons = $.timelapseJpeg.e.find('.frameIcons')
|
||||
$.timelapseJpeg.fieldHolder = $.timelapseJpeg.e.find('.fieldHolder')
|
||||
$.timelapseJpeg.frameStripPreview = $.timelapseJpeg.e.find('.frameStripPreview')
|
||||
$.timelapseJpeg.frameStripContainer = $.timelapseJpeg.e.find('.frameStripContainer')
|
||||
$.timelapseJpeg.playBackViewImg = $.timelapseJpeg.e.find('.playBackView img')
|
||||
$.timelapseJpeg.liveStreamView = $.timelapseJpeg.e.find('.liveStreamView')
|
||||
$.timelapseJpeg.monitors=$.timelapseJpeg.e.find('.monitors_list')
|
||||
$.timelapseJpeg.pointer = $.ccio.init('location',$user)
|
||||
$.timelapseJpeg.downloadRecheckTimers = {}
|
||||
$.timelapseJpeg.selectedStartDate = moment().utc().subtract(2, 'days').format('YYYY-MM-DDTHH:mm:ss')
|
||||
$.timelapseJpeg.selectedEndDate = moment().utc().format('YYYY-MM-DDTHH:mm:ss')
|
||||
$.timelapseJpeg.datepicker.daterangepicker({
|
||||
startDate: moment().utc().subtract(2, 'days'),
|
||||
endDate: moment().utc(),
|
||||
timePicker: true,
|
||||
locale: {
|
||||
format: 'YYYY/MM/DD hh:mm:ss A'
|
||||
}
|
||||
}, function(start, end, label) {
|
||||
console.log(start,end)
|
||||
var selectedStartDate = moment(start).utc().format('YYYY-MM-DDTHH:mm:ss')
|
||||
var selectedEndDate = moment(end).utc().format('YYYY-MM-DDTHH:mm:ss')
|
||||
$.timelapseJpeg.draw(selectedStartDate,selectedEndDate)
|
||||
$.timelapseJpeg.selectedStartDate = selectedStartDate
|
||||
$.timelapseJpeg.selectedEndDate = selectedEndDate
|
||||
})
|
||||
$.timelapseJpeg.monitors.change(function(){
|
||||
$.timelapseJpeg.draw()
|
||||
$.timelapseJpeg.getLiveStream()
|
||||
})
|
||||
$.timelapseJpeg.getLiveStream = function(){
|
||||
var selectedMonitor = $.timelapseJpeg.monitors.val()
|
||||
$.timelapseJpeg.liveStreamView.html(`<iframe src="${$.timelapseJpeg.pointer+$user.auth_token+'/embed/'+$user.ke+'/'+selectedMonitor+'/jquery|fullscreen'}"></iframe>`)
|
||||
$.timelapseJpeg.liveStreamView.find('iframe').width($.timelapseJpeg.playBackViewImg.width())
|
||||
|
||||
}
|
||||
$.timelapseJpeg.draw = function(startDate,endDate){
|
||||
if(!startDate)startDate = $.timelapseJpeg.selectedStartDate
|
||||
if(!endDate)endDate = $.timelapseJpeg.selectedEndDate
|
||||
$.timelapseJpeg.frameStripContainerOffset = $.timelapseJpeg.frameStripContainer.offset()
|
||||
var queryString = ['start=' + startDate,'end=' + endDate]
|
||||
var frameIconsHtml = ''
|
||||
var selectedMonitor = $.timelapseJpeg.monitors.val()
|
||||
var apiURL = $.timelapseJpeg.pointer+$user.auth_token+'/timelapse/'+$user.ke+'/'+selectedMonitor
|
||||
console.log(apiURL + '?' + queryString.join('&'))
|
||||
$.getJSON(apiURL + '?' + queryString.join('&'),function(data){
|
||||
if(data && data[0]){
|
||||
var firstFilename = data[0].filename
|
||||
$.timelapseJpeg.frameSelected = firstFilename
|
||||
$.timelapseJpeg.playlist = {}
|
||||
$.timelapseJpeg.playlistArray = []
|
||||
$.each(data.reverse(),function(n,fileInfo){
|
||||
fileInfo.href = apiURL + '/' + fileInfo.filename.split('T')[0] + '/' + fileInfo.filename
|
||||
fileInfo.number = n
|
||||
frameIconsHtml += '<div class="col-md-4"><div class="frame" data-filename="' + fileInfo.filename + '" style="background-image:url(\'' + fileInfo.href + '\')"><div class="shade">' + moment(fileInfo.time).format('YYYY-MM-DD HH:mm:ss') + '</div></div></div>'
|
||||
$.timelapseJpeg.playlist[fileInfo.filename] = fileInfo
|
||||
})
|
||||
$.timelapseJpeg.playlistArray = data
|
||||
$.timelapseJpeg.frameIcons.html(frameIconsHtml)
|
||||
$.timelapseJpeg.frameIcons.find(`.frame:first`).click()
|
||||
$.timelapseJpeg.getLiveStream()
|
||||
$.timelapseJpeg.resetFilmStripPositions()
|
||||
}else{
|
||||
frameIconsHtml = lang['No Data']
|
||||
$.timelapseJpeg.frameIcons.html(frameIconsHtml)
|
||||
}
|
||||
})
|
||||
}
|
||||
$.timelapseJpeg.fieldHolderCssHeightModifier = 0
|
||||
$.timelapseJpeg.resetFilmStripPositions = function(){
|
||||
var numberOfFrames = Object.keys($.timelapseJpeg.playlist).length
|
||||
var fieldHolderHeight = $.timelapseJpeg.fieldHolder.height() + $.timelapseJpeg.fieldHolderCssHeightModifier
|
||||
console.log("calc(100% - " + fieldHolderHeight + "px)")
|
||||
$.timelapseJpeg.frameIcons.css({height:"calc(100% - " + fieldHolderHeight + "px)"})
|
||||
}
|
||||
$.timelapseJpeg.setPlayBackFrame = function(href){
|
||||
$.timelapseJpeg.playBackViewImg[0].src = href
|
||||
}
|
||||
$.timelapseJpeg.playInterval = 1000 / 30
|
||||
$.timelapseJpeg.play = function(){
|
||||
var selectedFrame = $.timelapseJpeg.playlist[$.timelapseJpeg.frameSelected]
|
||||
var selectedFrameNumber = $.timelapseJpeg.playlist[$.timelapseJpeg.frameSelected].number
|
||||
$.timelapseJpeg.setPlayBackFrame(selectedFrame.href)
|
||||
$.timelapseJpeg.frameIcons.find(`.frame.selected`).removeClass('selected')
|
||||
$.timelapseJpeg.frameIcons.find(`.frame[data-filename="${selectedFrame.filename}"]`).addClass('selected')
|
||||
clearTimeout($.timelapseJpeg.playIntervalTimer)
|
||||
$.timelapseJpeg.playIntervalTimer = setTimeout(function(){
|
||||
++selectedFrameNumber
|
||||
var newSelectedFrame = $.timelapseJpeg.playlistArray[selectedFrameNumber]
|
||||
if(!newSelectedFrame)return
|
||||
$.timelapseJpeg.frameSelected = newSelectedFrame.filename
|
||||
$.timelapseJpeg.play()
|
||||
},$.timelapseJpeg.playInterval)
|
||||
}
|
||||
$.timelapseJpeg.destroy = function(){
|
||||
$.timelapseJpeg.pause()
|
||||
$.timelapseJpeg.frameIcons.empty()
|
||||
$.timelapseJpeg.setPlayBackFrame(null)
|
||||
}
|
||||
$.timelapseJpeg.pause = function(){
|
||||
clearTimeout($.timelapseJpeg.playIntervalTimer)
|
||||
delete($.timelapseJpeg.playIntervalTimer)
|
||||
}
|
||||
$.timelapseJpeg.togglePlayPause = function(){
|
||||
if($.timelapseJpeg.playIntervalTimer){
|
||||
$.timelapseJpeg.pause()
|
||||
}else{
|
||||
$.timelapseJpeg.play()
|
||||
}
|
||||
}
|
||||
$.timelapseJpeg.e.on('click','.frame',function(){
|
||||
$.timelapseJpeg.pause()
|
||||
var selectedFrame = $(this).attr('data-filename')
|
||||
if(selectedFrame === $.timelapseJpeg.frameSelected){
|
||||
return $.timelapseJpeg.togglePlayPause()
|
||||
}
|
||||
$.timelapseJpeg.frameSelected = selectedFrame
|
||||
$.timelapseJpeg.frameIcons.find(`.frame.selected`).removeClass('selected')
|
||||
$.timelapseJpeg.frameIcons.find(`.frame[data-filename="${selectedFrame}"]`).addClass('selected')
|
||||
var href = $.timelapseJpeg.playlist[selectedFrame].href
|
||||
$.timelapseJpeg.setPlayBackFrame(href)
|
||||
})
|
||||
$.timelapseJpeg.e.on('click','.download_mp4',function(){
|
||||
var _this = $(this)
|
||||
var runDownloader = function(){
|
||||
var startDate = $.timelapseJpeg.selectedStartDate
|
||||
var endDate = $.timelapseJpeg.selectedEndDate
|
||||
var queryString = ['fps=' + $.timelapseJpeg.timelapseJpegFps.val(),'start=' + startDate,'end=' + endDate,'mp4=1']
|
||||
var timerId = queryString.join('&')
|
||||
var selectedMonitor = $.timelapseJpeg.monitors.val()
|
||||
var generatorUrl = $.timelapseJpeg.pointer + $user.auth_token + '/timelapse/' + $user.ke + '/' + selectedMonitor
|
||||
$.getJSON(generatorUrl + '?' + queryString.join('&'),function(response){
|
||||
if(response.fileExists){
|
||||
_this.text(lang['Download'])
|
||||
var downloadName = startDate + '_' + endDate + '_' + selectedMonitor + '.mp4'
|
||||
var a = document.createElement('a')
|
||||
a.href = generatorUrl + '?' + queryString.concat(['download="1"']).join('&')
|
||||
a.download = downloadName
|
||||
a.click()
|
||||
}else{
|
||||
_this.html(' <i class="fa fa-spinner fa-pulse"></i> ')
|
||||
clearTimeout($.timelapseJpeg.downloadRecheckTimers[timerId])
|
||||
$.timelapseJpeg.downloadRecheckTimers[timerId] = setTimeout(function(){
|
||||
runDownloader()
|
||||
},5000)
|
||||
}
|
||||
})
|
||||
}
|
||||
runDownloader()
|
||||
})
|
||||
$.timelapseJpeg.e.on('shown.bs.modal', function (e) {
|
||||
// $.timelapseJpeg.datepicker.val($.timelapseJpeg.baseDate)
|
||||
// $.timelapseJpeg.draw($.timelapseJpeg.baseDate)
|
||||
})
|
||||
$.timelapseJpeg.e.on('hidden.bs.modal', function (e) {
|
||||
$.timelapseJpeg.destroy()
|
||||
})
|
||||
$.timelapseJpeg.timelapseJpegFps
|
||||
.on('slide',function(ev){
|
||||
$.timelapseJpeg.playInterval = 1000 / ev.value
|
||||
})
|
||||
.slider({
|
||||
formatter: function(value) {
|
||||
return 'FPS : ' + value;
|
||||
}
|
||||
})
|
||||
})
|
|
@ -3,9 +3,80 @@ $(document).ready(function(e){
|
|||
$.sM={e:$('#settings')};
|
||||
$.sM.f=$.sM.e.find('form');
|
||||
$.sM.links=$('#linkShinobi');
|
||||
$.sM.addStorageMaxAmounts=$('#add_storage_max_amounts')
|
||||
$.sM.addStorageMaxAmountsField=$.sM.e.find('[detail="addStorage"]')
|
||||
$.sM.g=$('#settings_mon_groups');
|
||||
$.sM.md=$.sM.f.find('[detail]');
|
||||
$.sM.md.change($.ccio.form.details);
|
||||
$.sM.e.find('.follow-list ul').affix();
|
||||
$.sM.sections = {}
|
||||
var addSection = function(section){
|
||||
if(!section.id){
|
||||
var userSettingsId = section.name.replace(/[^a-zA-Z ]/g, '').replace(/[^a-zA-Z ]/g, '').replace(/ /g, '')
|
||||
section.id = userSettingsId
|
||||
}
|
||||
$.sM.sections[section.name] = section.id
|
||||
if(section.info){
|
||||
$.each(section.info,function(m,block){
|
||||
if(block.isFormGroupGroup === true){
|
||||
addSection(block)
|
||||
}
|
||||
})
|
||||
}
|
||||
if(section.blocks){
|
||||
$.each(section.blocks,function(m,block){
|
||||
addSection(block)
|
||||
})
|
||||
}
|
||||
}
|
||||
$.each($.ccio.definitions['Account Settings'].blocks,function(n,section){
|
||||
addSection(section)
|
||||
})
|
||||
$.sM.drawList = function(){
|
||||
var list = $.sM.e.find('.follow-list ul')
|
||||
var html = ''
|
||||
$.each($.sM.sections,function(sectionName,sectionId){
|
||||
var el = $('#' + sectionId)
|
||||
if(el.length > 0){
|
||||
html += '<li><a class="scrollTo" href="#' + sectionId + '" scrollToParent="#settings .modal-body">' + sectionName + '</a></li>'
|
||||
}
|
||||
})
|
||||
list.html(html)
|
||||
}
|
||||
try{
|
||||
var addStorageData = JSON.parse($user.details.addStorage || '{}')
|
||||
var html = ''
|
||||
$.each(addStorage,function(n,storage){
|
||||
var limit = ""
|
||||
if(addStorageData[storage.path] && addStorageData[storage.path].limit){
|
||||
limit = addStorageData[storage.path].limit
|
||||
}
|
||||
html += `<div class="form-group">\
|
||||
<label><div><span>${lang['Max Storage Amount']} : ${storage.name}</span></div>\
|
||||
<div><input class="form-control" addStorageLimit="${storage.path}" value="${limit}"></div>\
|
||||
</label>\
|
||||
</div>`
|
||||
})
|
||||
$.sM.addStorageMaxAmounts.html(html)
|
||||
$.sM.addStorageMaxAmounts.on('change','[addStorageLimit]',function(){
|
||||
var json = {}
|
||||
$.each(addStorage,function(n,storage){
|
||||
var storageId = storage.path
|
||||
var el = $.sM.addStorageMaxAmounts.find('[addStorageLimit="' + storageId + '"]')
|
||||
var value = el.val()
|
||||
json[storageId] = {
|
||||
name: storage.name,
|
||||
path: storage.path,
|
||||
limit: value
|
||||
}
|
||||
})
|
||||
$.sM.addStorageMaxAmountsField.val(JSON.stringify(json))
|
||||
})
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
$.sM.drawList()
|
||||
$.sM.f.find('[selector]').change(function(e){
|
||||
e.v=$(this).val();e.a=$(this).attr('selector')
|
||||
$.sM.f.find('.'+e.a+'_input').hide()
|
||||
|
|
|
@ -109,19 +109,15 @@ $.vidview.e.find('.export_selected').click(function(){
|
|||
$.confirm.e.modal('show');
|
||||
$.confirm.title.text(lang['Export Selected Videos'])
|
||||
var html = lang.ExportSelectedVideosMsg+'<div style="margin-bottom:15px"></div>'
|
||||
var selectedVideos = []
|
||||
$.each(videos,function(n,v){
|
||||
html+=v.filename+'<br>';
|
||||
selectedVideos.push($.vidview.loadedVideos[v.filename])
|
||||
})
|
||||
$.confirm.body.html(html)
|
||||
$.confirm.click({title:'Export Video',class:'btn-danger'},function(){
|
||||
var queryVariables = []
|
||||
queryVariables.push('videos='+JSON.stringify(videos))
|
||||
if($.ccio.useUTC === true){
|
||||
queryVariables.push('isUTC=true')
|
||||
}
|
||||
var downloadZip = $.ccio.init('location',$user)+$user.auth_token+'/zipVideos/'+$user.ke+'?'+queryVariables.join('&')
|
||||
$('#temp').html('<iframe>a</iframe>').find('iframe').attr('src',downloadZip);
|
||||
});
|
||||
$.zipVideosAndDownload(selectedVideos)
|
||||
})
|
||||
})
|
||||
$.vidview.e.find('.merge_selected').click(function(){
|
||||
e = {}
|
||||
|
|
13
web/libs/js/jszip.min.js
vendored
Normal file
13
web/libs/js/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/libs/js/moment-with-locales.min.js
vendored
Normal file
1
web/libs/js/moment-with-locales.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
47
web/libs/js/vis.min.js
vendored
Normal file
47
web/libs/js/vis.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -15,34 +15,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
//confirmwindow
|
||||
$.confirm={e:$('#confirm_window')};
|
||||
$.confirm.title=$.confirm.e.find('.modal-title span')
|
||||
$.confirm.body=$.confirm.e.find('.modal-body')
|
||||
$.confirm.footer=$.confirm.e.find('.modal-footer')
|
||||
$.confirm.click=function(x,e){
|
||||
$.confirm.footer.find('.confirmaction').remove()
|
||||
var createButton = function(x,place,callback){
|
||||
$.confirm.footer.prepend('<button type="button" class="btn '+x.class+' confirmaction confirmaction'+place+'">'+x.title+'</button>')
|
||||
if(!x.class){x.class='btn-success'}
|
||||
if(!x.title){x.title='Save changes'}
|
||||
$.confirm.footer.find('.confirmaction'+place).click(function(){
|
||||
$.confirm.e.modal('hide')
|
||||
callback();
|
||||
})
|
||||
}
|
||||
if(x instanceof Array){
|
||||
$.each(x,function(place,x){
|
||||
createButton(x,place,x.callback)
|
||||
})
|
||||
}else{
|
||||
createButton(x,0,e)
|
||||
}
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$.confirm.e.on('hidden.bs.modal', function () {
|
||||
$.confirm.footer.find('.confirmaction').remove()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<script src="<%-window.libURL%>libs/js/dash2.confirm.js"></script>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue