mirror of
https://github.com/kerberos-io/agent.git
synced 2026-10-25 18:38:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f681cfe1 | ||
|
|
f935360fda | ||
|
|
71cd315142 | ||
|
|
d9694ac1a3 | ||
|
|
08f589586d | ||
|
|
192f78ae78 | ||
|
|
ef20d4c0b1 | ||
|
|
af95c0f798 | ||
|
|
0e32a10ff5 | ||
|
|
e59c2b179d | ||
|
|
e5d03f19de | ||
|
|
58c3e73f6f | ||
|
|
8ca2c44422 | ||
|
|
a2e584a225 | ||
|
|
c4cda0afb0 | ||
|
|
adbb923e92 | ||
|
|
f444ae4ad6 | ||
|
|
9fa9538320 | ||
|
|
943e81000e | ||
|
|
b16d028293 | ||
|
|
07646e483d | ||
|
|
36b93a34b4 | ||
|
|
b0d2409524 | ||
|
|
be7a231950 | ||
|
|
31a0b9efa4 | ||
|
|
d70a3ed343 | ||
|
|
56cebb6451 | ||
|
|
99f61bc5e8 | ||
|
|
a5d02e3275 | ||
|
|
354ab7db05 | ||
|
|
dc817f8c26 | ||
|
|
a90097731c | ||
|
|
5b3bbbb37e | ||
|
|
4a4aabd71c | ||
|
|
b058c1e742 | ||
|
|
7671b1c2c3 | ||
|
|
4cc8135e1a | ||
|
|
3cb38099ea | ||
|
|
deb0308dc4 | ||
|
|
24c729eea3 | ||
|
|
c59d511ea3 | ||
|
|
6f8745dc3a |
@@ -1,2 +1,2 @@
|
|||||||
FROM kerberos/devcontainer:9da0ee3
|
FROM kerberos/devcontainer:b2bc659
|
||||||
LABEL AUTHOR=Kerberos.io
|
LABEL AUTHOR=Kerberos.io
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
architecture: [arm64, arm/v7, arm/v6]
|
#architecture: [arm64, arm/v7, arm/v6]
|
||||||
|
architecture: [arm64, arm/v7]
|
||||||
steps:
|
steps:
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
architecture: [arm64, arm-v7, arm-v6]
|
architecture: [arm64, arm-v7, arm-v6]
|
||||||
|
#architecture: [arm64, arm-v7]
|
||||||
steps:
|
steps:
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
|
|||||||
@@ -162,9 +162,13 @@ Next to attaching the configuration file, it is also possible to override the co
|
|||||||
| `AGENT_KEY` | A unique identifier for your Kerberos Agent, this is auto-generated but can be overriden. | "" |
|
| `AGENT_KEY` | A unique identifier for your Kerberos Agent, this is auto-generated but can be overriden. | "" |
|
||||||
| `AGENT_NAME` | The agent friendly-name. | "agent" |
|
| `AGENT_NAME` | The agent friendly-name. | "agent" |
|
||||||
| `AGENT_TIMEZONE` | Timezone which is used for converting time. | "Africa/Ceuta" |
|
| `AGENT_TIMEZONE` | Timezone which is used for converting time. | "Africa/Ceuta" |
|
||||||
|
| `AGENT_REMOVE_AFTER_UPLOAD` | When enabled, recordings uploaded successfully to a storage will be removed from disk. | "true" |
|
||||||
| `AGENT_OFFLINE` | Makes sure no external connection is made. | "false" |
|
| `AGENT_OFFLINE` | Makes sure no external connection is made. | "false" |
|
||||||
| `AGENT_AUTO_CLEAN` | Cleans up the recordings directory. | "true" |
|
| `AGENT_AUTO_CLEAN` | Cleans up the recordings directory. | "true" |
|
||||||
| `AGENT_AUTO_CLEAN_MAX_SIZE` | If `AUTO_CLEAN` enabled, set the max size of the recordings directory in (MB). | "100" |
|
| `AGENT_AUTO_CLEAN_MAX_SIZE` | If `AUTO_CLEAN` enabled, set the max size of the recordings directory in (MB). | "100" |
|
||||||
|
| `AGENT_TIME` | Enable the timetable for Kerberos Agent | "false" |
|
||||||
|
| `AGENT_TIMETABLE` | A (weekly) time table to specify when to make recordings "start1,end1,start2,end2;start1.. | "" |
|
||||||
|
| `AGENT_REGION_POLYGON` | A single polygon set for motion detection: "x1,y1;x2,y2;x3,y3;... | "" |
|
||||||
| `AGENT_CAPTURE_IPCAMERA_RTSP` | Full-HD RTSP endpoint to the camera you're targetting. | "" |
|
| `AGENT_CAPTURE_IPCAMERA_RTSP` | Full-HD RTSP endpoint to the camera you're targetting. | "" |
|
||||||
| `AGENT_CAPTURE_IPCAMERA_SUB_RTSP` | Sub-stream RTSP endpoint used for livestreaming (WebRTC). | "" |
|
| `AGENT_CAPTURE_IPCAMERA_SUB_RTSP` | Sub-stream RTSP endpoint used for livestreaming (WebRTC). | "" |
|
||||||
| `AGENT_CAPTURE_IPCAMERA_ONVIF` | Mark as a compliant ONVIF device. | "" |
|
| `AGENT_CAPTURE_IPCAMERA_ONVIF` | Mark as a compliant ONVIF device. | "" |
|
||||||
@@ -179,20 +183,20 @@ Next to attaching the configuration file, it is also possible to override the co
|
|||||||
| `AGENT_CAPTURE_PIXEL_CHANGE` | If `CONTINUOUS` set to `false`, the number of pixel require to change before motion triggers. | "150" |
|
| `AGENT_CAPTURE_PIXEL_CHANGE` | If `CONTINUOUS` set to `false`, the number of pixel require to change before motion triggers. | "150" |
|
||||||
| `AGENT_CAPTURE_FRAGMENTED` | Set the format of the recorded MP4 to fragmented (suitable for HLS). | "false" |
|
| `AGENT_CAPTURE_FRAGMENTED` | Set the format of the recorded MP4 to fragmented (suitable for HLS). | "false" |
|
||||||
| `AGENT_CAPTURE_FRAGMENTED_DURATION` | If `AGENT_CAPTURE_FRAGMENTED` set to `true`, define the duration (seconds) of a fragment. | "8" |
|
| `AGENT_CAPTURE_FRAGMENTED_DURATION` | If `AGENT_CAPTURE_FRAGMENTED` set to `true`, define the duration (seconds) of a fragment. | "8" |
|
||||||
|
| `AGENT_MQTT_URI` | A MQTT broker endpoint that is used for bi-directional communication (live view, onvif, etc) | "tcp://mqtt.kerberos.io:1883" |
|
||||||
|
| `AGENT_MQTT_USERNAME` | Username of the MQTT broker. | "" |
|
||||||
|
| `AGENT_MQTT_PASSWORD` | Password of the MQTT broker. | "" |
|
||||||
|
| `AGENT_STUN_URI` | When using WebRTC, you'll need to provide a STUN server. | "stun:turn.kerberos.io:8443" |
|
||||||
|
| `AGENT_TURN_URI` | When using WebRTC, you'll need to provide a TURN server. | "turn:turn.kerberos.io:8443" |
|
||||||
|
| `AGENT_TURN_USERNAME` | TURN username used for WebRTC. | "username1" |0
|
||||||
|
| `AGENT_TURN_PASSWORD` | TURN password used for WebRTC. | "password1" |
|
||||||
| `AGENT_CLOUD` | Store recordings in a Kerberos Hub (s3) or your Kerberos Vault (kstorage). | "s3" |
|
| `AGENT_CLOUD` | Store recordings in a Kerberos Hub (s3) or your Kerberos Vault (kstorage). | "s3" |
|
||||||
| `AGENT_HUB_URI` | The Kerberos Hub API, defaults to our Kerberos Hub SAAS. | "https://api.cloud.kerberos.io" |
|
| `AGENT_HUB_URI` | The Kerberos Hub API, defaults to our Kerberos Hub SAAS. | "https://api.cloud.kerberos.io" |
|
||||||
| `AGENT_HUB_KEY` | The access key linked to your account in Kerberos Hub. | "" |
|
| `AGENT_HUB_KEY` | The access key linked to your account in Kerberos Hub. | "" |
|
||||||
| `AGENT_HUB_PRIVATE_KEY` | The secret access key linked to your account in Kerberos Hub. | "" |
|
| `AGENT_HUB_PRIVATE_KEY` | The secret access key linked to your account in Kerberos Hub. | "" |
|
||||||
| `AGENT_HUB_USERNAME` | Your Kerberos Hub username, which owns the above access and secret keys. | "" |
|
| `AGENT_HUB_USERNAME` | Your Kerberos Hub username, which owns the above access and secret keys. | "" |
|
||||||
| `AGENT_HUB_SITE` | The site ID of a site you've created in your Kerberos Hub account. | "" |
|
| `AGENT_HUB_SITE` | The site ID of a site you've created in your Kerberos Hub account. | "" |
|
||||||
| `AGENT_MQTT_URI` | A MQTT broker endpoint that is used for bi-directional communication (live view, onvif, etc) | "tcp://mqtt.kerberos.io:1883" |
|
| `AGENT_KERBEROSVAULT_URI` | The Kerberos Vault API url. | "" |
|
||||||
| `AGENT_MQTT_USERNAME` | Username of the MQTT broker. | "" |
|
|
||||||
| `AGENT_MQTT_PASSWORD` | Password of the MQTT broker. | "" |
|
|
||||||
| `AGENT_STUN_URI` | When using WebRTC, you'll need to provide a STUN server. | "stun:turn.kerberos.io:8443" |
|
|
||||||
| `AGENT_TURN_URI` | When using WebRTC, you'll need to provide a TURN server. | "turn:turn.kerberos.io:8443" |
|
|
||||||
| `AGENT_TURN_USERNAME` | TURN username used for WebRTC. | "username1" |
|
|
||||||
| `AGENT_TURN_PASSWORD` | TURN password used for WebRTC. | "password1" |
|
|
||||||
| `AGENT_KERBEROSVAULT_URI` | The Kerberos Vault API url. | "" . |
|
|
||||||
| `AGENT_KERBEROSVAULT_ACCESS_KEY` | The access key of a Kerberos Vault account. | "" |
|
| `AGENT_KERBEROSVAULT_ACCESS_KEY` | The access key of a Kerberos Vault account. | "" |
|
||||||
| `AGENT_KERBEROSVAULT_SECRET_KEY` | The secret key of a Kerberos Vault account. | "" |
|
| `AGENT_KERBEROSVAULT_SECRET_KEY` | The secret key of a Kerberos Vault account. | "" |
|
||||||
| `AGENT_KERBEROSVAULT_PROVIDER` | A Kerberos Vault provider you have created (optional). | "" |
|
| `AGENT_KERBEROSVAULT_PROVIDER` | A Kerberos Vault provider you have created (optional). | "" |
|
||||||
@@ -210,6 +214,43 @@ After a few minutes, you will see a beautiful `Visual Studio Code` shown in your
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
On opening of the GitHub Codespace, some dependencies will be installed. Once this is done go ahead to the `ui/src/config.json` file, and (un)comment following section. Make sure to replace the `externalHost` variable with the DNS name you will retrieve from the next step.
|
||||||
|
|
||||||
|
// Uncomment this when using codespaces or other special DNS names (which you can't control)
|
||||||
|
// replace this with the DNS name of the kerberos agent server (the codespace url)
|
||||||
|
const externalHost = 'cedricve-automatic-computing-machine-v647rxvj4whx9qp-80.preview.app.github.dev';
|
||||||
|
|
||||||
|
const dev = {
|
||||||
|
ENV: 'dev',
|
||||||
|
HOSTNAME: externalHost,
|
||||||
|
//API_URL: `${protocol}//${hostname}:8080/api`,
|
||||||
|
//URL: `${protocol}//${hostname}:8080`,
|
||||||
|
//WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
|
||||||
|
|
||||||
|
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
|
||||||
|
API_URL: `${protocol}//${externalHost}/api`,
|
||||||
|
URL: `${protocol}//${externalHost}`,
|
||||||
|
WS_URL: `${websocketprotocol}//${externalHost}/ws`,
|
||||||
|
};
|
||||||
|
|
||||||
|
Go and open two terminals one for the `ui` project and one for the `machinery` project.
|
||||||
|
|
||||||
|
1. Terminal A:
|
||||||
|
|
||||||
|
cd machinery/
|
||||||
|
go run main.go run camera 80
|
||||||
|
|
||||||
|
2. Terminal B:
|
||||||
|
|
||||||
|
cd ui/
|
||||||
|
yarn start
|
||||||
|
|
||||||
|
Once executed, a popup will show up mentioning `portforwarding`. You should see two ports being opened, one for the ui `3000` and one for the machinery `80`. `Right-click` on the port `80` and change visibility from `private` to `public`, this is required to avoid `CORS` errors.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As mentioned above, copy the hostname of the `machinery` DNS name, and past it in the `ui/src/config.json` file. Once done reload, the `ui` page in your browser, and you should be able to access the login page with the default credentials `root` and `root`.
|
||||||
|
|
||||||
## Develop and build
|
## Develop and build
|
||||||
|
|
||||||
Kerberos Agent is divided in two parts a `machinery` and `web`. Both parts live in this repository in their relative folders. For development or running the application on your local machine, you have to run both the `machinery` and the `web` as described below. When running in production everything is shipped as only one artifact, read more about this at [Building for production](#building-for-production).
|
Kerberos Agent is divided in two parts a `machinery` and `web`. Both parts live in this repository in their relative folders. For development or running the application on your local machine, you have to run both the `machinery` and the `web` as described below. When running in production everything is shipped as only one artifact, read more about this at [Building for production](#building-for-production).
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"type": "",
|
"type": "",
|
||||||
"key": "",
|
"key": "",
|
||||||
@@ -5,6 +6,7 @@
|
|||||||
"time": "false",
|
"time": "false",
|
||||||
"offline": "false",
|
"offline": "false",
|
||||||
"auto_clean": "true",
|
"auto_clean": "true",
|
||||||
|
"remove_after_upload": "true",
|
||||||
"max_directory_size": 100,
|
"max_directory_size": 100,
|
||||||
"timezone": "Africa/Ceuta",
|
"timezone": "Africa/Ceuta",
|
||||||
"capture": {
|
"capture": {
|
||||||
@@ -21,6 +23,10 @@
|
|||||||
"device": ""
|
"device": ""
|
||||||
},
|
},
|
||||||
"continuous": "false",
|
"continuous": "false",
|
||||||
|
"recording": "true",
|
||||||
|
"snapshots": "true",
|
||||||
|
"liveview": "true",
|
||||||
|
"motion": "true",
|
||||||
"postrecording": 20,
|
"postrecording": 20,
|
||||||
"prerecording": 10,
|
"prerecording": 10,
|
||||||
"maxlengthrecording": 30,
|
"maxlengthrecording": 30,
|
||||||
|
|||||||
+3
-2
@@ -2,7 +2,8 @@ module github.com/kerberos-io/agent/machinery
|
|||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
//replace github.com/kerberos-io/joy4 v1.0.51 => ../../../../github.com/kerberos-io/joy4
|
//replace github.com/kerberos-io/joy4 v1.0.54 => ../../../../github.com/kerberos-io/joy4
|
||||||
|
|
||||||
//replace github.com/kerberos-io/onvif v0.0.5 => ../../../../github.com/kerberos-io/onvif
|
//replace github.com/kerberos-io/onvif v0.0.5 => ../../../../github.com/kerberos-io/onvif
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -20,7 +21,7 @@ require (
|
|||||||
github.com/golang-module/carbon/v2 v2.2.3
|
github.com/golang-module/carbon/v2 v2.2.3
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/kellydunn/golang-geo v0.7.0
|
github.com/kellydunn/golang-geo v0.7.0
|
||||||
github.com/kerberos-io/joy4 v1.0.53
|
github.com/kerberos-io/joy4 v1.0.55
|
||||||
github.com/kerberos-io/onvif v0.0.5
|
github.com/kerberos-io/onvif v0.0.5
|
||||||
github.com/minio/minio-go/v6 v6.0.57
|
github.com/minio/minio-go/v6 v6.0.57
|
||||||
github.com/nsmith5/mjpeg v0.0.0-20200913181537-54b8ada0e53e
|
github.com/nsmith5/mjpeg v0.0.0-20200913181537-54b8ada0e53e
|
||||||
|
|||||||
+2
-2
@@ -175,8 +175,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/kellydunn/golang-geo v0.7.0 h1:A5j0/BvNgGwY6Yb6inXQxzYwlPHc6WVZR+MrarZYNNg=
|
github.com/kellydunn/golang-geo v0.7.0 h1:A5j0/BvNgGwY6Yb6inXQxzYwlPHc6WVZR+MrarZYNNg=
|
||||||
github.com/kellydunn/golang-geo v0.7.0/go.mod h1:YYlQPJ+DPEzrHx8kT3oPHC/NjyvCCXE+IuKGKdrjrcU=
|
github.com/kellydunn/golang-geo v0.7.0/go.mod h1:YYlQPJ+DPEzrHx8kT3oPHC/NjyvCCXE+IuKGKdrjrcU=
|
||||||
github.com/kerberos-io/joy4 v1.0.53 h1:DfVptCUzo/77xLUIwnp1/dbcVffmT0DKPDduQBcu26Y=
|
github.com/kerberos-io/joy4 v1.0.55 h1:P5RISBp8kUowgb/bvqLPVKPJL9n9jI/wXBCLs+XFMWg=
|
||||||
github.com/kerberos-io/joy4 v1.0.53/go.mod h1:nZp4AjvKvTOXRrmDyAIOw+Da+JA5OcSo/JundGfOlFU=
|
github.com/kerberos-io/joy4 v1.0.55/go.mod h1:nZp4AjvKvTOXRrmDyAIOw+Da+JA5OcSo/JundGfOlFU=
|
||||||
github.com/kerberos-io/onvif v0.0.5 h1:kq9mnHZkih9Jl4DyIJ4Rzt++Y3DDKy3nI8S2ESEfZ5w=
|
github.com/kerberos-io/onvif v0.0.5 h1:kq9mnHZkih9Jl4DyIJ4Rzt++Y3DDKy3nI8S2ESEfZ5w=
|
||||||
github.com/kerberos-io/onvif v0.0.5/go.mod h1:Hr2dJOH2LM5SpYKk17gYZ1CMjhGhUl+QlT5kwYogrW0=
|
github.com/kerberos-io/onvif v0.0.5/go.mod h1:Hr2dJOH2LM5SpYKk17gYZ1CMjhGhUl+QlT5kwYogrW0=
|
||||||
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||||
|
|||||||
+15
-14
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
service := os.Getenv("DATADOG_AGENT_SERVICE")
|
service := os.Getenv("DATADOG_AGENT_SERVICE")
|
||||||
environment := os.Getenv("DATADOG_AGENT_ENVIRONMENT")
|
environment := os.Getenv("DATADOG_AGENT_ENVIRONMENT")
|
||||||
fmt.Println("Starting Datadog Agent with service: " + service + " and environment: " + environment)
|
log.Log.Info("Starting Datadog Agent with service: " + service + " and environment: " + environment)
|
||||||
rules := []tracer.SamplingRule{tracer.RateRule(1)}
|
rules := []tracer.SamplingRule{tracer.RateRule(1)}
|
||||||
tracer.Start(
|
tracer.Start(
|
||||||
tracer.WithSamplingRules(rules),
|
tracer.WithSamplingRules(rules),
|
||||||
@@ -60,25 +59,20 @@ func main() {
|
|||||||
case "version":
|
case "version":
|
||||||
log.Log.Info("You are currrently running Kerberos Agent " + VERSION)
|
log.Log.Info("You are currrently running Kerberos Agent " + VERSION)
|
||||||
|
|
||||||
case "pending-upload":
|
|
||||||
name := os.Args[2]
|
|
||||||
fmt.Println(name)
|
|
||||||
|
|
||||||
case "discover":
|
case "discover":
|
||||||
timeout := os.Args[2]
|
timeout := os.Args[2]
|
||||||
fmt.Println(timeout)
|
log.Log.Info(timeout)
|
||||||
|
|
||||||
case "run":
|
case "run":
|
||||||
{
|
{
|
||||||
name := os.Args[2]
|
name := os.Args[2]
|
||||||
port := os.Args[3]
|
port := os.Args[3]
|
||||||
|
|
||||||
// Check the folder permissions, it might be that we do not have permissions to write
|
// Print Kerberos.io ASCII art
|
||||||
// recordings, update the configuration or save snapshots.
|
utils.PrintASCIIArt()
|
||||||
err := utils.CheckDataDirectoryPermissions()
|
|
||||||
if err != nil {
|
// Print the environment variables which include "AGENT_" as prefix.
|
||||||
log.Log.Fatal(err.Error())
|
utils.PrintEnvironmentVariables()
|
||||||
}
|
|
||||||
|
|
||||||
// Read the config on start, and pass it to the other
|
// Read the config on start, and pass it to the other
|
||||||
// function and features. Please note that this might be changed
|
// function and features. Please note that this might be changed
|
||||||
@@ -93,6 +87,13 @@ func main() {
|
|||||||
// We will override the configuration with the environment variables
|
// We will override the configuration with the environment variables
|
||||||
components.OverrideWithEnvironmentVariables(&configuration)
|
components.OverrideWithEnvironmentVariables(&configuration)
|
||||||
|
|
||||||
|
// Printing final configuration
|
||||||
|
utils.PrintConfiguration(&configuration)
|
||||||
|
|
||||||
|
// Check the folder permissions, it might be that we do not have permissions to write
|
||||||
|
// recordings, update the configuration or save snapshots.
|
||||||
|
utils.CheckDataDirectoryPermissions()
|
||||||
|
|
||||||
// Set timezone
|
// Set timezone
|
||||||
timezone, _ := time.LoadLocation(configuration.Config.Timezone)
|
timezone, _ := time.LoadLocation(configuration.Config.Timezone)
|
||||||
log.Log.Init(timezone)
|
log.Log.Init(timezone)
|
||||||
@@ -120,6 +121,6 @@ func main() {
|
|||||||
routers.StartWebserver(&configuration, &communication)
|
routers.StartWebserver(&configuration, &communication)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Println("Sorry I don't understand :(")
|
log.Log.Error("Main: Sorry I don't understand :(")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,19 @@ func OpenRTSP(url string) (av.DemuxCloser, []av.CodecData, error) {
|
|||||||
return nil, []av.CodecData{}, err
|
return nil, []av.CodecData{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetVideoDecoder(streams []av.CodecData) *ffmpeg.VideoDecoder {
|
func GetVideoStream(streams []av.CodecData) (av.CodecData, error) {
|
||||||
|
var videoStream av.CodecData
|
||||||
|
for _, stream := range streams {
|
||||||
|
if stream.Type().IsAudio() {
|
||||||
|
//astream := stream.(av.AudioCodecData)
|
||||||
|
} else if stream.Type().IsVideo() {
|
||||||
|
videoStream = stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return videoStream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVideoDecoder(decoder *ffmpeg.VideoDecoder, streams []av.CodecData) {
|
||||||
// Load video codec
|
// Load video codec
|
||||||
var vstream av.VideoCodecData
|
var vstream av.VideoCodecData
|
||||||
for _, stream := range streams {
|
for _, stream := range streams {
|
||||||
@@ -35,8 +47,10 @@ func GetVideoDecoder(streams []av.CodecData) *ffmpeg.VideoDecoder {
|
|||||||
vstream = stream.(av.VideoCodecData)
|
vstream = stream.(av.VideoCodecData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dec, _ := ffmpeg.NewVideoDecoder(vstream)
|
err := ffmpeg.NewVideoDecoder(decoder, vstream)
|
||||||
return dec
|
if err != nil {
|
||||||
|
log.Log.Error("GetVideoDecoder: " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeImage(frame *ffmpeg.VideoFrame, pkt av.Packet, decoder *ffmpeg.VideoDecoder, decoderMutex *sync.Mutex) (*ffmpeg.VideoFrame, error) {
|
func DecodeImage(frame *ffmpeg.VideoFrame, pkt av.Packet, decoder *ffmpeg.VideoDecoder, decoderMutex *sync.Mutex) (*ffmpeg.VideoFrame, error) {
|
||||||
@@ -46,60 +60,6 @@ func DecodeImage(frame *ffmpeg.VideoFrame, pkt av.Packet, decoder *ffmpeg.VideoD
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStreamInsights(infile av.DemuxCloser, streams []av.CodecData) (int, int, int, int) {
|
|
||||||
var width, height, fps, gopsize int
|
|
||||||
for _, stream := range streams {
|
|
||||||
if stream.Type().IsAudio() {
|
|
||||||
//astream := stream.(av.AudioCodecData)
|
|
||||||
} else if stream.Type().IsVideo() {
|
|
||||||
vstream := stream.(av.VideoCodecData)
|
|
||||||
width = vstream.Width()
|
|
||||||
height = vstream.Height()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for timeout := time.After(1 * time.Second); ; {
|
|
||||||
var err error
|
|
||||||
if _, err = infile.ReadPacket(); err != nil { // sometimes this throws an end of file..
|
|
||||||
log.Log.Error("HandleStream: " + err.Error())
|
|
||||||
}
|
|
||||||
fps++
|
|
||||||
select {
|
|
||||||
case <-timeout:
|
|
||||||
break loop
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gopCounter := 0
|
|
||||||
start := false
|
|
||||||
for {
|
|
||||||
var pkt av.Packet
|
|
||||||
var err error
|
|
||||||
if pkt, err = infile.ReadPacket(); err != nil { // sometimes this throws an end of file..
|
|
||||||
log.Log.Error("HandleStream: " + err.Error())
|
|
||||||
}
|
|
||||||
// Could be that a decode is throwing errors.
|
|
||||||
if len(pkt.Data) > 0 {
|
|
||||||
if start {
|
|
||||||
gopCounter = gopCounter + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if pkt.IsKeyFrame {
|
|
||||||
if start == false {
|
|
||||||
start = true
|
|
||||||
} else {
|
|
||||||
gopsize = gopCounter
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return width, height, fps, gopsize
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleStream(infile av.DemuxCloser, queue *pubsub.Queue, communication *models.Communication) { //, wg *sync.WaitGroup) {
|
func HandleStream(infile av.DemuxCloser, queue *pubsub.Queue, communication *models.Communication) { //, wg *sync.WaitGroup) {
|
||||||
|
|
||||||
log.Log.Debug("HandleStream: started")
|
log.Log.Debug("HandleStream: started")
|
||||||
|
|||||||
@@ -272,6 +272,9 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
|
|||||||
var file *os.File
|
var file *os.File
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
var lastDuration time.Duration
|
||||||
|
var lastRecordingTime int64
|
||||||
|
|
||||||
for motion := range communication.HandleMotion {
|
for motion := range communication.HandleMotion {
|
||||||
|
|
||||||
timestamp = time.Now().Unix()
|
timestamp = time.Now().Unix()
|
||||||
@@ -281,7 +284,16 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
|
|||||||
// If we have prerecording we will substract the number of seconds.
|
// If we have prerecording we will substract the number of seconds.
|
||||||
// Taking into account FPS = GOP size (Keyfram interval)
|
// Taking into account FPS = GOP size (Keyfram interval)
|
||||||
if config.Capture.PreRecording > 0 {
|
if config.Capture.PreRecording > 0 {
|
||||||
startRecording = startRecording - int64(config.Capture.PreRecording) + 1
|
|
||||||
|
// Might be that recordings are coming short after each other.
|
||||||
|
// Therefore we do some math with the current time and the last recording time.
|
||||||
|
|
||||||
|
timeBetweenNowAndLastRecording := startRecording - lastRecordingTime
|
||||||
|
if timeBetweenNowAndLastRecording > int64(config.Capture.PreRecording) {
|
||||||
|
startRecording = startRecording - int64(config.Capture.PreRecording) + 1
|
||||||
|
} else {
|
||||||
|
startRecording = startRecording - timeBetweenNowAndLastRecording
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
||||||
@@ -351,7 +363,7 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
|
|||||||
log.Log.Info("HandleRecordStream: closing recording (timestamp: " + strconv.FormatInt(timestamp, 10) + ", recordingPeriod: " + strconv.FormatInt(recordingPeriod, 10) + ", now: " + strconv.FormatInt(now, 10) + ", startRecording: " + strconv.FormatInt(startRecording, 10) + ", maxRecordingPeriod: " + strconv.FormatInt(maxRecordingPeriod, 10))
|
log.Log.Info("HandleRecordStream: closing recording (timestamp: " + strconv.FormatInt(timestamp, 10) + ", recordingPeriod: " + strconv.FormatInt(recordingPeriod, 10) + ", now: " + strconv.FormatInt(now, 10) + ", startRecording: " + strconv.FormatInt(startRecording, 10) + ", maxRecordingPeriod: " + strconv.FormatInt(maxRecordingPeriod, 10))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if pkt.IsKeyFrame && !start {
|
if pkt.IsKeyFrame && !start && pkt.Time >= lastDuration {
|
||||||
log.Log.Info("HandleRecordStream: write frames")
|
log.Log.Info("HandleRecordStream: write frames")
|
||||||
start = true
|
start = true
|
||||||
}
|
}
|
||||||
@@ -378,6 +390,9 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
|
|||||||
myMuxer.WriteTrailerWithPacket(nextPkt)
|
myMuxer.WriteTrailerWithPacket(nextPkt)
|
||||||
log.Log.Info("HandleRecordStream: file save: " + name)
|
log.Log.Info("HandleRecordStream: file save: " + name)
|
||||||
|
|
||||||
|
lastDuration = pkt.Time
|
||||||
|
lastRecordingTime = time.Now().Unix()
|
||||||
|
|
||||||
// Cleanup muxer
|
// Cleanup muxer
|
||||||
myMuxer.Close()
|
myMuxer.Close()
|
||||||
myMuxer = nil
|
myMuxer = nil
|
||||||
|
|||||||
+163
-101
@@ -54,10 +54,11 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
|
|||||||
log.Log.Debug("HandleUpload: stopping as Offline is enabled.")
|
log.Log.Debug("HandleUpload: stopping as Offline is enabled.")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// Half a second delay between two uploads
|
||||||
|
delay := 500 * time.Millisecond
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
ff, err := utils.ReadDirectory(watchDirectory)
|
|
||||||
|
|
||||||
// This will check if we need to stop the thread,
|
// This will check if we need to stop the thread,
|
||||||
// because of a reconfiguration.
|
// because of a reconfiguration.
|
||||||
select {
|
select {
|
||||||
@@ -66,7 +67,10 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
|
|||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
ff, err := utils.ReadDirectory(watchDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Log.Error("HandleUpload: " + err.Error())
|
||||||
|
} else {
|
||||||
for _, f := range ff {
|
for _, f := range ff {
|
||||||
|
|
||||||
// This will check if we need to stop the thread,
|
// This will check if we need to stop the thread,
|
||||||
@@ -78,11 +82,42 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileName := f.Name()
|
fileName := f.Name()
|
||||||
|
uploaded := false
|
||||||
|
configured := false
|
||||||
|
err = nil
|
||||||
if config.Cloud == "s3" {
|
if config.Cloud == "s3" {
|
||||||
UploadS3(configuration, fileName, watchDirectory)
|
uploaded, configured, err = UploadS3(configuration, fileName)
|
||||||
} else if config.Cloud == "kstorage" {
|
} else if config.Cloud == "kstorage" {
|
||||||
UploadKerberosVault(configuration, fileName, watchDirectory)
|
uploaded, configured, err = UploadKerberosVault(configuration, fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the file is uploaded, if so, remove it.
|
||||||
|
if uploaded {
|
||||||
|
delay = 500 * time.Millisecond // reset
|
||||||
|
err := os.Remove(watchDirectory + fileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Log.Error("HandleUpload: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we need to remove the original recording
|
||||||
|
// removeAfterUpload is set to false by default
|
||||||
|
if config.RemoveAfterUpload == "true" {
|
||||||
|
err := os.Remove("./data/recordings/" + fileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Log.Error("HandleUpload: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !configured {
|
||||||
|
err := os.Remove(watchDirectory + fileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Log.Error("HandleUpload: " + err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delay = 5 * time.Second // slow down
|
||||||
|
log.Log.Error("HandleUpload: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,6 +130,9 @@ func GetSystemInfo() (models.System, error) {
|
|||||||
var usedMem uint64 = 0
|
var usedMem uint64 = 0
|
||||||
var totalMem uint64 = 0
|
var totalMem uint64 = 0
|
||||||
var freeMem uint64 = 0
|
var freeMem uint64 = 0
|
||||||
|
|
||||||
|
var processUsedMem uint64 = 0
|
||||||
|
|
||||||
architecture := ""
|
architecture := ""
|
||||||
cpuId := ""
|
cpuId := ""
|
||||||
KernelVersion := ""
|
KernelVersion := ""
|
||||||
@@ -133,18 +171,27 @@ func GetSystemInfo() (models.System, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process, err := sysinfo.Self()
|
||||||
|
if err == nil {
|
||||||
|
memInfo, err := process.Memory()
|
||||||
|
if err == nil {
|
||||||
|
processUsedMem = memInfo.Resident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
system := models.System{
|
system := models.System{
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
CPUId: cpuId,
|
CPUId: cpuId,
|
||||||
KernelVersion: KernelVersion,
|
KernelVersion: KernelVersion,
|
||||||
Version: agentVersion,
|
Version: agentVersion,
|
||||||
MACs: MACs,
|
MACs: MACs,
|
||||||
IPs: IPs,
|
IPs: IPs,
|
||||||
BootTime: uint64(bootTime.Unix()),
|
BootTime: uint64(bootTime.Unix()),
|
||||||
Architecture: architecture,
|
Architecture: architecture,
|
||||||
UsedMemory: usedMem,
|
UsedMemory: usedMem,
|
||||||
TotalMemory: totalMem,
|
TotalMemory: totalMem,
|
||||||
FreeMemory: freeMem,
|
FreeMemory: freeMem,
|
||||||
|
ProcessUsedMemory: processUsedMem,
|
||||||
}
|
}
|
||||||
|
|
||||||
return system, nil
|
return system, nil
|
||||||
@@ -184,109 +231,124 @@ func HandleHeartBeat(configuration *models.Configuration, communication *models.
|
|||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Check if we have a friendly name or not.
|
if key != "" {
|
||||||
name := config.Name
|
// Check if we have a friendly name or not.
|
||||||
if config.FriendlyName != "" {
|
name := config.Name
|
||||||
name = config.FriendlyName
|
if config.FriendlyName != "" {
|
||||||
}
|
name = config.FriendlyName
|
||||||
|
}
|
||||||
|
|
||||||
// Get some system information
|
// Get some system information
|
||||||
// like the uptime, hostname, memory usage, etc.
|
// like the uptime, hostname, memory usage, etc.
|
||||||
system, _ := GetSystemInfo()
|
system, _ := GetSystemInfo()
|
||||||
|
|
||||||
// We will formated the uptime to a human readable format
|
// We will formated the uptime to a human readable format
|
||||||
// this will be used on Kerberos Hub: Uptime -> 1 day and 2 hours.
|
// this will be used on Kerberos Hub: Uptime -> 1 day and 2 hours.
|
||||||
uptimeFormatted := uptimeStart.Format("2006-01-02 15:04:05")
|
uptimeFormatted := uptimeStart.Format("2006-01-02 15:04:05")
|
||||||
uptimeString := carbon.Parse(uptimeFormatted).DiffForHumans()
|
uptimeString := carbon.Parse(uptimeFormatted).DiffForHumans()
|
||||||
uptimeString = strings.ReplaceAll(uptimeString, "ago", "")
|
uptimeString = strings.ReplaceAll(uptimeString, "ago", "")
|
||||||
|
|
||||||
// We'll check which mode is enabled for the camera.
|
// Do the same for boottime
|
||||||
onvifEnabled := "false"
|
bootTimeFormatted := time.Unix(int64(system.BootTime), 0).Format("2006-01-02 15:04:05")
|
||||||
if config.Capture.IPCamera.ONVIFXAddr != "" {
|
boottimeString := carbon.Parse(bootTimeFormatted).DiffForHumans()
|
||||||
device, err := onvif.ConnectToOnvifDevice(configuration)
|
boottimeString = strings.ReplaceAll(boottimeString, "ago", "")
|
||||||
if err == nil {
|
|
||||||
capabilities := onvif.GetCapabilitiesFromDevice(device)
|
// We'll check which mode is enabled for the camera.
|
||||||
for _, v := range capabilities {
|
onvifEnabled := "false"
|
||||||
if v == "PTZ" || v == "ptz" {
|
if config.Capture.IPCamera.ONVIFXAddr != "" {
|
||||||
onvifEnabled = "true"
|
device, err := onvif.ConnectToOnvifDevice(configuration)
|
||||||
|
if err == nil {
|
||||||
|
capabilities := onvif.GetCapabilitiesFromDevice(device)
|
||||||
|
for _, v := range capabilities {
|
||||||
|
if v == "PTZ" || v == "ptz" {
|
||||||
|
onvifEnabled = "true"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the agent is running inside a cluster (Kerberos Factory) or as
|
// Check if the agent is running inside a cluster (Kerberos Factory) or as
|
||||||
// an open source agent
|
// an open source agent
|
||||||
isEnterprise := false
|
isEnterprise := false
|
||||||
if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" {
|
if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" {
|
||||||
isEnterprise = true
|
isEnterprise = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var object = fmt.Sprintf(`{
|
// Congert to string
|
||||||
"key" : "%s",
|
macs, _ := json.Marshal(system.MACs)
|
||||||
"version" : "3.0.0",
|
ips, _ := json.Marshal(system.IPs)
|
||||||
"release" : "%s",
|
|
||||||
"cpuid" : "%s",
|
|
||||||
"clouduser" : "%s",
|
|
||||||
"cloudpublickey" : "%s",
|
|
||||||
"cameraname" : "%s",
|
|
||||||
"enterprise" : %t,
|
|
||||||
"hostname" : "%s",
|
|
||||||
"architecture" : "%s",
|
|
||||||
"totalMemory" : "%d",
|
|
||||||
"usedMemory" : "%d",
|
|
||||||
"freeMemory" : "%d",
|
|
||||||
"macs" : "%v",
|
|
||||||
"ips" : "%v",
|
|
||||||
"board" : "",
|
|
||||||
"disk1size" : "%s",
|
|
||||||
"disk3size" : "%s",
|
|
||||||
"diskvdasize" : "%s",
|
|
||||||
"uptime" : "%s",
|
|
||||||
"siteID" : "%s",
|
|
||||||
"onvif" : "%s",
|
|
||||||
"numberoffiles" : "33",
|
|
||||||
"timestamp" : 1564747908,
|
|
||||||
"cameratype" : "IPCamera",
|
|
||||||
"docker" : true,
|
|
||||||
"kios" : false,
|
|
||||||
"raspberrypi" : false
|
|
||||||
}`, config.Key, system.Version, system.CPUId, username, key, name, isEnterprise, system.Hostname, system.Architecture, system.TotalMemory, system.UsedMemory, system.FreeMemory, system.MACs, system.IPs, "0", "0", "0", uptimeString, config.HubSite, onvifEnabled)
|
|
||||||
|
|
||||||
var jsonStr = []byte(object)
|
var object = fmt.Sprintf(`{
|
||||||
buffy := bytes.NewBuffer(jsonStr)
|
"key" : "%s",
|
||||||
req, _ := http.NewRequest("POST", url, buffy)
|
"version" : "3.0.0",
|
||||||
req.Header.Set("Content-Type", "application/json")
|
"release" : "%s",
|
||||||
|
"cpuid" : "%s",
|
||||||
|
"clouduser" : "%s",
|
||||||
|
"cloudpublickey" : "%s",
|
||||||
|
"cameraname" : "%s",
|
||||||
|
"enterprise" : %t,
|
||||||
|
"hostname" : "%s",
|
||||||
|
"architecture" : "%s",
|
||||||
|
"totalMemory" : "%d",
|
||||||
|
"usedMemory" : "%d",
|
||||||
|
"freeMemory" : "%d",
|
||||||
|
"processMemory" : "%d",
|
||||||
|
"mac_list" : %s,
|
||||||
|
"ip_list" : %s,
|
||||||
|
"board" : "",
|
||||||
|
"disk1size" : "%s",
|
||||||
|
"disk3size" : "%s",
|
||||||
|
"diskvdasize" : "%s",
|
||||||
|
"uptime" : "%s",
|
||||||
|
"boot_time" : "%s",
|
||||||
|
"siteID" : "%s",
|
||||||
|
"onvif" : "%s",
|
||||||
|
"numberoffiles" : "33",
|
||||||
|
"timestamp" : 1564747908,
|
||||||
|
"cameratype" : "IPCamera",
|
||||||
|
"docker" : true,
|
||||||
|
"kios" : false,
|
||||||
|
"raspberrypi" : false
|
||||||
|
}`, config.Key, system.Version, system.CPUId, username, key, name, isEnterprise, system.Hostname, system.Architecture, system.TotalMemory, system.UsedMemory, system.FreeMemory, system.ProcessUsedMemory, macs, ips, "0", "0", "0", uptimeString, boottimeString, config.HubSite, onvifEnabled)
|
||||||
|
|
||||||
client := &http.Client{}
|
var jsonStr = []byte(object)
|
||||||
resp, err := client.Do(req)
|
buffy := bytes.NewBuffer(jsonStr)
|
||||||
if resp != nil {
|
req, _ := http.NewRequest("POST", url, buffy)
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
if err == nil && resp.StatusCode == 200 {
|
|
||||||
communication.CloudTimestamp.Store(time.Now().Unix())
|
|
||||||
log.Log.Info("HandleHeartBeat: (200) Heartbeat received by Kerberos Hub.")
|
|
||||||
} else {
|
|
||||||
log.Log.Error("HandleHeartBeat: (400) Something went wrong while sending to Kerberos Hub.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a Kerberos Vault connected, we will also send some analytics
|
|
||||||
// to that service.
|
|
||||||
vaultURI = config.KStorage.URI
|
|
||||||
if vaultURI != "" {
|
|
||||||
buffy = bytes.NewBuffer(jsonStr)
|
|
||||||
req, _ = http.NewRequest("POST", vaultURI+"/devices/heartbeat", buffy)
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
client = &http.Client{}
|
client := &http.Client{}
|
||||||
resp, err = client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
}
|
}
|
||||||
if err == nil && resp.StatusCode == 200 {
|
if err == nil && resp.StatusCode == 200 {
|
||||||
log.Log.Info("HandleHeartBeat: (200) Heartbeat received by Kerberos Vault.")
|
communication.CloudTimestamp.Store(time.Now().Unix())
|
||||||
|
log.Log.Info("HandleHeartBeat: (200) Heartbeat received by Kerberos Hub.")
|
||||||
} else {
|
} else {
|
||||||
log.Log.Error("HandleHeartBeat: (400) Something went wrong while sending to Kerberos Vault.")
|
log.Log.Error("HandleHeartBeat: (400) Something went wrong while sending to Kerberos Hub.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have a Kerberos Vault connected, we will also send some analytics
|
||||||
|
// to that service.
|
||||||
|
vaultURI = config.KStorage.URI
|
||||||
|
if vaultURI != "" {
|
||||||
|
buffy = bytes.NewBuffer(jsonStr)
|
||||||
|
req, _ = http.NewRequest("POST", vaultURI+"/devices/heartbeat", buffy)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client = &http.Client{}
|
||||||
|
resp, err = client.Do(req)
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
if err == nil && resp.StatusCode == 200 {
|
||||||
|
log.Log.Info("HandleHeartBeat: (200) Heartbeat received by Kerberos Vault.")
|
||||||
|
} else {
|
||||||
|
log.Log.Error("HandleHeartBeat: (400) Something went wrong while sending to Kerberos Vault.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Log.Error("HandleHeartBeat: Disabled as we do not have a public key defined.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will check if we need to stop the thread,
|
// This will check if we need to stop the thread,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cloud
|
package cloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -9,19 +10,19 @@ import (
|
|||||||
"github.com/kerberos-io/agent/machinery/src/models"
|
"github.com/kerberos-io/agent/machinery/src/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UploadKerberosVault(configuration *models.Configuration, fileName string, directory string) bool {
|
func UploadKerberosVault(configuration *models.Configuration, fileName string) (bool, bool, error) {
|
||||||
|
|
||||||
config := configuration.Config
|
config := configuration.Config
|
||||||
|
|
||||||
if config.KStorage.AccessKey == "" ||
|
if config.KStorage.AccessKey == "" ||
|
||||||
config.KStorage.SecretAccessKey == "" ||
|
config.KStorage.SecretAccessKey == "" ||
|
||||||
config.KStorage.Provider == "" ||
|
|
||||||
config.KStorage.Directory == "" ||
|
config.KStorage.Directory == "" ||
|
||||||
config.KStorage.URI == "" {
|
config.KStorage.URI == "" {
|
||||||
log.Log.Info("UploadKerberosVault: Kerberos Vault not properly configured.")
|
err := "UploadKerberosVault: Kerberos Vault not properly configured."
|
||||||
|
log.Log.Info(err)
|
||||||
|
return false, false, errors.New(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println("Uploading...")
|
|
||||||
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
||||||
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
||||||
// - Timestamp
|
// - Timestamp
|
||||||
@@ -30,21 +31,20 @@ func UploadKerberosVault(configuration *models.Configuration, fileName string, d
|
|||||||
// - Region
|
// - Region
|
||||||
// - Number of changes
|
// - Number of changes
|
||||||
// - Token
|
// - Token
|
||||||
|
|
||||||
// KerberosCloud, this means storage is disabled and proxy enabled.
|
// KerberosCloud, this means storage is disabled and proxy enabled.
|
||||||
log.Log.Info("UploadKerberosVault: Uploading to Kerberos Vault (" + config.KStorage.URI + ")")
|
log.Log.Info("UploadKerberosVault: Uploading to Kerberos Vault (" + config.KStorage.URI + ")")
|
||||||
|
|
||||||
log.Log.Info("UploadKerberosVault: Upload started for " + fileName)
|
log.Log.Info("UploadKerberosVault: Upload started for " + fileName)
|
||||||
fullname := "data/recordings/" + fileName
|
fullname := "data/recordings/" + fileName
|
||||||
|
|
||||||
file, err := os.OpenFile(fullname, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fullname, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if file != nil {
|
||||||
log.Log.Info("UploadKerberosVault: Upload Failed, file doesn't exists anymore.")
|
defer file.Close()
|
||||||
os.Remove(directory + "/" + fileName)
|
}
|
||||||
return false
|
if err != nil {
|
||||||
|
err := "UploadKerberosVault: Upload Failed, file doesn't exists anymore."
|
||||||
|
log.Log.Info(err)
|
||||||
|
return false, true, errors.New(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
publicKey := config.KStorage.CloudKey
|
publicKey := config.KStorage.CloudKey
|
||||||
// This is the new way ;)
|
// This is the new way ;)
|
||||||
@@ -54,7 +54,9 @@ func UploadKerberosVault(configuration *models.Configuration, fileName string, d
|
|||||||
|
|
||||||
req, err := http.NewRequest("POST", config.KStorage.URI+"/storage", file)
|
req, err := http.NewRequest("POST", config.KStorage.URI+"/storage", file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error reading request. " + err.Error())
|
errorMessage := "UploadKerberosVault: error reading request, " + config.KStorage.URI + "/storage: " + err.Error()
|
||||||
|
log.Log.Error(errorMessage)
|
||||||
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "video/mp4")
|
req.Header.Set("Content-Type", "video/mp4")
|
||||||
req.Header.Set("X-Kerberos-Storage-CloudKey", publicKey)
|
req.Header.Set("X-Kerberos-Storage-CloudKey", publicKey)
|
||||||
@@ -65,11 +67,9 @@ func UploadKerberosVault(configuration *models.Configuration, fileName string, d
|
|||||||
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
req.Header.Set("X-Kerberos-Storage-Device", config.Key)
|
||||||
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
req.Header.Set("X-Kerberos-Storage-Capture", "IPCamera")
|
||||||
req.Header.Set("X-Kerberos-Storage-Directory", config.KStorage.Directory)
|
req.Header.Set("X-Kerberos-Storage-Directory", config.KStorage.Directory)
|
||||||
//client := &http.Client{Timeout: time.Second * 30}
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
}
|
}
|
||||||
@@ -80,17 +80,16 @@ func UploadKerberosVault(configuration *models.Configuration, fileName string, d
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
log.Log.Info("UploadKerberosVault: Upload Finished, " + resp.Status + ", " + string(body))
|
log.Log.Info("UploadKerberosVault: Upload Finished, " + resp.Status + ", " + string(body))
|
||||||
// We will remove the file from disk as well
|
return true, true, nil
|
||||||
os.Remove(fullname)
|
|
||||||
os.Remove(directory + "/" + fileName)
|
|
||||||
} else {
|
} else {
|
||||||
log.Log.Info("UploadKerberosVault: Upload Failed, " + resp.Status + ", " + string(body))
|
log.Log.Info("UploadKerberosVault: Upload Failed, " + resp.Status + ", " + string(body))
|
||||||
|
return false, true, nil
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.Log.Info("UploadKerberosVault: Upload Failed, " + err.Error())
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
|
errorMessage := "UploadKerberosVault: Upload Failed, " + err.Error()
|
||||||
|
log.Log.Info(errorMessage)
|
||||||
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-19
@@ -2,6 +2,7 @@ package cloud
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,11 +14,10 @@ import (
|
|||||||
"github.com/minio/minio-go/v6"
|
"github.com/minio/minio-go/v6"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UploadS3(configuration *models.Configuration, fileName string, directory string) bool {
|
func UploadS3(configuration *models.Configuration, fileName string) (bool, bool, error) {
|
||||||
|
|
||||||
config := configuration.Config
|
config := configuration.Config
|
||||||
|
|
||||||
//fmt.Println("Uploading...")
|
|
||||||
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
// timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token
|
||||||
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
// 1564859471_6-474162_oprit_577-283-727-375_1153_27.mp4
|
||||||
// - Timestamp
|
// - Timestamp
|
||||||
@@ -28,10 +28,12 @@ func UploadS3(configuration *models.Configuration, fileName string, directory st
|
|||||||
// - Token
|
// - Token
|
||||||
|
|
||||||
if config.S3 == nil {
|
if config.S3 == nil {
|
||||||
log.Log.Error("UploadS3: Uploading Failed, as no settings found")
|
errorMessage := "UploadS3: Uploading Failed, as no settings found"
|
||||||
return false
|
log.Log.Error(errorMessage)
|
||||||
|
return false, false, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legacy support, should get rid of it!
|
||||||
aws_access_key_id := config.S3.Publickey
|
aws_access_key_id := config.S3.Publickey
|
||||||
aws_secret_access_key := config.S3.Secretkey
|
aws_secret_access_key := config.S3.Secretkey
|
||||||
aws_region := config.S3.Region
|
aws_region := config.S3.Region
|
||||||
@@ -44,9 +46,18 @@ func UploadS3(configuration *models.Configuration, fileName string, directory st
|
|||||||
aws_secret_access_key = config.HubPrivateKey
|
aws_secret_access_key = config.HubPrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we have some credentials otherwise we abort the request.
|
||||||
|
if aws_access_key_id == "" || aws_secret_access_key == "" {
|
||||||
|
errorMessage := "UploadS3: Uploading Failed, as no credentials found"
|
||||||
|
log.Log.Error(errorMessage)
|
||||||
|
return false, false, errors.New(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
s3Client, err := minio.NewWithRegion("s3.amazonaws.com", aws_access_key_id, aws_secret_access_key, true, aws_region)
|
s3Client, err := minio.NewWithRegion("s3.amazonaws.com", aws_access_key_id, aws_secret_access_key, true, aws_region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err.Error())
|
errorMessage := "UploadS3: " + err.Error()
|
||||||
|
log.Log.Error(errorMessage)
|
||||||
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to use the proxy.
|
// Check if we need to use the proxy.
|
||||||
@@ -62,9 +73,9 @@ func UploadS3(configuration *models.Configuration, fileName string, directory st
|
|||||||
|
|
||||||
fileParts := strings.Split(fileName, "_")
|
fileParts := strings.Split(fileName, "_")
|
||||||
if len(fileParts) == 1 {
|
if len(fileParts) == 1 {
|
||||||
log.Log.Error("ERROR: " + fileName + " is not a valid name.")
|
errorMessage := "UploadS3: " + fileName + " is not a valid name."
|
||||||
os.Remove(directory + "/" + fileName)
|
log.Log.Error(errorMessage)
|
||||||
return false
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceKey := config.Key
|
deviceKey := config.Key
|
||||||
@@ -78,18 +89,21 @@ func UploadS3(configuration *models.Configuration, fileName string, directory st
|
|||||||
fullname := "data/recordings/" + fileName
|
fullname := "data/recordings/" + fileName
|
||||||
|
|
||||||
file, err := os.OpenFile(fullname, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fullname, os.O_RDWR, 0755)
|
||||||
defer file.Close()
|
if file != nil {
|
||||||
|
defer file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("UploadS3: " + err.Error())
|
errorMessage := "UploadS3: " + err.Error()
|
||||||
os.Remove(directory + "/" + fileName)
|
log.Log.Error(errorMessage)
|
||||||
return false
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInfo, err := file.Stat()
|
fileInfo, err := file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("UploadS3: " + err.Error())
|
errorMessage := "UploadS3: " + err.Error()
|
||||||
os.Remove(directory + "/" + fileName)
|
log.Log.Error(errorMessage)
|
||||||
return false
|
return false, true, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := s3Client.PutObject(config.S3.Bucket,
|
n, err := s3Client.PutObject(config.S3.Bucket,
|
||||||
@@ -113,11 +127,11 @@ func UploadS3(configuration *models.Configuration, fileName string, directory st
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("UploadS3: Uploading Failed, " + err.Error())
|
errorMessage := "UploadS3: Uploading Failed, " + err.Error()
|
||||||
return false
|
log.Log.Error(errorMessage)
|
||||||
|
return false, true, errors.New(errorMessage)
|
||||||
} else {
|
} else {
|
||||||
log.Log.Info("UploadS3: Upload Finished, file has been uploaded to bucket: " + strconv.FormatInt(n, 10))
|
log.Log.Info("UploadS3: Upload Finished, file has been uploaded to bucket: " + strconv.FormatInt(n, 10))
|
||||||
os.Remove(directory + "/" + fileName)
|
return true, true, nil
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package components
|
package components
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -42,24 +39,6 @@ func GetImageFromFilePath() (image.Image, error) {
|
|||||||
return nil, errors.New("Could not find a snapshot in " + snapshotDirectory)
|
return nil, errors.New("Could not find a snapshot in " + snapshotDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSnapshot() string {
|
|
||||||
var snapshot string
|
|
||||||
files, err := ioutil.ReadDir("./data/snapshots")
|
|
||||||
if err == nil && len(files) > 1 {
|
|
||||||
sort.Slice(files, func(i, j int) bool {
|
|
||||||
return files[i].ModTime().Before(files[j].ModTime())
|
|
||||||
})
|
|
||||||
f, _ := os.Open("./data/snapshots/" + files[1].Name())
|
|
||||||
defer f.Close()
|
|
||||||
// Read entire JPG into byte slice.
|
|
||||||
reader := bufio.NewReader(f)
|
|
||||||
content, _ := ioutil.ReadAll(reader)
|
|
||||||
// Encode as base64.
|
|
||||||
snapshot = base64.StdEncoding.EncodeToString(content)
|
|
||||||
}
|
|
||||||
return snapshot
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadUserConfig Reads the user configuration of the Kerberos Open Source instance.
|
// ReadUserConfig Reads the user configuration of the Kerberos Open Source instance.
|
||||||
// This will return a models.User struct including the username, password,
|
// This will return a models.User struct including the username, password,
|
||||||
// selected language, and if the installation was completed or not.
|
// selected language, and if the installation was completed or not.
|
||||||
@@ -67,15 +46,14 @@ func ReadUserConfig() (userConfig models.User) {
|
|||||||
for {
|
for {
|
||||||
jsonFile, err := os.Open("./data/config/user.json")
|
jsonFile, err := os.Open("./data/config/user.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
log.Log.Error("Config file is not found " + "./data/config/user.json, trying again in 5s: " + err.Error())
|
||||||
fmt.Println("Config file is not found " + "./data/config/user.json" + ", trying again in 5s.")
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Successfully Opened user.json")
|
log.Log.Info("Successfully Opened user.json")
|
||||||
byteValue, _ := ioutil.ReadAll(jsonFile)
|
byteValue, _ := ioutil.ReadAll(jsonFile)
|
||||||
err = json.Unmarshal(byteValue, &userConfig)
|
err = json.Unmarshal(byteValue, &userConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("JSON file not valid: " + err.Error())
|
log.Log.Error("JSON file not valid: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
jsonFile.Close()
|
jsonFile.Close()
|
||||||
break
|
break
|
||||||
@@ -146,6 +124,9 @@ func OpenConfig(configuration *models.Configuration) {
|
|||||||
conjungo.Merge(&s3, configuration.CustomConfig.S3, opts)
|
conjungo.Merge(&s3, configuration.CustomConfig.S3, opts)
|
||||||
configuration.Config.S3 = &s3
|
configuration.Config.S3 = &s3
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
opts = nil
|
||||||
|
|
||||||
} else if os.Getenv("DEPLOYMENT") == "" || os.Getenv("DEPLOYMENT") == "agent" {
|
} else if os.Getenv("DEPLOYMENT") == "" || os.Getenv("DEPLOYMENT") == "agent" {
|
||||||
|
|
||||||
// Local deployment means we do a stand-alone installation
|
// Local deployment means we do a stand-alone installation
|
||||||
@@ -163,11 +144,11 @@ func OpenConfig(configuration *models.Configuration) {
|
|||||||
err = json.Unmarshal(byteValue, &configuration.Config)
|
err = json.Unmarshal(byteValue, &configuration.Config)
|
||||||
jsonFile.Close()
|
jsonFile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("JSON file not valid: " + err.Error())
|
log.Log.Error("JSON file not valid: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
err = json.Unmarshal(byteValue, &configuration.CustomConfig)
|
err = json.Unmarshal(byteValue, &configuration.CustomConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("JSON file not valid: " + err.Error())
|
log.Log.Error("JSON file not valid: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -287,26 +268,82 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
/* Cloud settings for persisting recordings */
|
/* Conditions */
|
||||||
case "AGENT_CLOUD":
|
|
||||||
configuration.Config.Cloud = value
|
case "AGENT_TIME":
|
||||||
|
configuration.Config.Time = value
|
||||||
|
break
|
||||||
|
case "AGENT_TIMETABLE":
|
||||||
|
var timetable []*models.Timetable
|
||||||
|
|
||||||
|
// Convert value to timetable array with (start1, end1, start2, end2)
|
||||||
|
// Where days are limited by ; and time by ,
|
||||||
|
// su;mo;tu;we;th;fr;sa
|
||||||
|
// 0,43199,43200,86400;0,43199,43200,86400
|
||||||
|
|
||||||
|
// Split days
|
||||||
|
daysString := strings.Split(value, ";")
|
||||||
|
for _, dayString := range daysString {
|
||||||
|
// Split time
|
||||||
|
timeString := strings.Split(dayString, ",")
|
||||||
|
if len(timeString) == 4 {
|
||||||
|
start1, err := strconv.ParseInt(timeString[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
end1, err := strconv.ParseInt(timeString[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
start2, err := strconv.ParseInt(timeString[2], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
end2, err := strconv.ParseInt(timeString[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
timetable = append(timetable, &models.Timetable{
|
||||||
|
Start1: int(start1),
|
||||||
|
End1: int(end1),
|
||||||
|
Start2: int(start2),
|
||||||
|
End2: int(end2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configuration.Config.Timetable = timetable
|
||||||
break
|
break
|
||||||
|
|
||||||
/* When connected and storing in Kerberos Hub (SAAS) */
|
case "AGENT_REGION_POLYGON":
|
||||||
case "AGENT_HUB_URI":
|
var coordinates []models.Coordinate
|
||||||
configuration.Config.HubURI = value
|
|
||||||
break
|
// Convert value to coordinates array
|
||||||
case "AGENT_HUB_KEY":
|
// 0,0;1,1;2,2;3,3
|
||||||
configuration.Config.HubKey = value
|
coordinatesString := strings.Split(value, ";")
|
||||||
break
|
for _, coordinateString := range coordinatesString {
|
||||||
case "AGENT_HUB_PRIVATE_KEY":
|
coordinate := strings.Split(coordinateString, ",")
|
||||||
configuration.Config.HubPrivateKey = value
|
if len(coordinate) == 2 {
|
||||||
break
|
x, err := strconv.ParseFloat(coordinate[0], 64)
|
||||||
case "AGENT_HUB_USERNAME":
|
if err != nil {
|
||||||
configuration.Config.S3.Username = value
|
continue
|
||||||
break
|
}
|
||||||
case "AGENT_HUB_SITE":
|
y, err := strconv.ParseFloat(coordinate[1], 64)
|
||||||
configuration.Config.HubSite = value
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
coordinates = append(coordinates, models.Coordinate{
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.Config.Region.Polygon = []models.Polygon{
|
||||||
|
{
|
||||||
|
Coordinates: coordinates,
|
||||||
|
ID: "0",
|
||||||
|
},
|
||||||
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
/* MQTT settings for bi-directional communication */
|
/* MQTT settings for bi-directional communication */
|
||||||
@@ -334,6 +371,32 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
|||||||
configuration.Config.TURNPassword = value
|
configuration.Config.TURNPassword = value
|
||||||
break
|
break
|
||||||
|
|
||||||
|
/* Cloud settings for persisting recordings */
|
||||||
|
case "AGENT_CLOUD":
|
||||||
|
configuration.Config.Cloud = value
|
||||||
|
break
|
||||||
|
|
||||||
|
case "AGENT_REMOVE_AFTER_UPLOAD":
|
||||||
|
configuration.Config.RemoveAfterUpload = value
|
||||||
|
break
|
||||||
|
|
||||||
|
/* When connected and storing in Kerberos Hub (SAAS) */
|
||||||
|
case "AGENT_HUB_URI":
|
||||||
|
configuration.Config.HubURI = value
|
||||||
|
break
|
||||||
|
case "AGENT_HUB_KEY":
|
||||||
|
configuration.Config.HubKey = value
|
||||||
|
break
|
||||||
|
case "AGENT_HUB_PRIVATE_KEY":
|
||||||
|
configuration.Config.HubPrivateKey = value
|
||||||
|
break
|
||||||
|
case "AGENT_HUB_USERNAME":
|
||||||
|
configuration.Config.S3.Username = value
|
||||||
|
break
|
||||||
|
case "AGENT_HUB_SITE":
|
||||||
|
configuration.Config.HubSite = value
|
||||||
|
break
|
||||||
|
|
||||||
/* When storing in a Kerberos Vault */
|
/* When storing in a Kerberos Vault */
|
||||||
case "AGENT_KERBEROSVAULT_URI":
|
case "AGENT_KERBEROSVAULT_URI":
|
||||||
configuration.Config.KStorage.URI = value
|
configuration.Config.KStorage.URI = value
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package components
|
package components
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kerberos-io/joy4/cgo/ffmpeg"
|
||||||
|
|
||||||
"github.com/kerberos-io/agent/machinery/src/capture"
|
"github.com/kerberos-io/agent/machinery/src/capture"
|
||||||
"github.com/kerberos-io/agent/machinery/src/cloud"
|
"github.com/kerberos-io/agent/machinery/src/cloud"
|
||||||
"github.com/kerberos-io/agent/machinery/src/computervision"
|
"github.com/kerberos-io/agent/machinery/src/computervision"
|
||||||
@@ -15,7 +18,6 @@ import (
|
|||||||
routers "github.com/kerberos-io/agent/machinery/src/routers/mqtt"
|
routers "github.com/kerberos-io/agent/machinery/src/routers/mqtt"
|
||||||
"github.com/kerberos-io/joy4/av"
|
"github.com/kerberos-io/joy4/av"
|
||||||
"github.com/kerberos-io/joy4/av/pubsub"
|
"github.com/kerberos-io/joy4/av/pubsub"
|
||||||
"github.com/kerberos-io/joy4/cgo/ffmpeg"
|
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -57,12 +59,17 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
|
|||||||
// do several checks to see if the agent is still operational.
|
// do several checks to see if the agent is still operational.
|
||||||
go ControlAgent(communication)
|
go ControlAgent(communication)
|
||||||
|
|
||||||
|
// Create some global variables
|
||||||
|
decoder := &ffmpeg.VideoDecoder{}
|
||||||
|
subDecoder := &ffmpeg.VideoDecoder{}
|
||||||
|
cameraSettings := &models.Camera{}
|
||||||
|
|
||||||
// Run the agent and fire up all the other
|
// Run the agent and fire up all the other
|
||||||
// goroutines which do image capture, motion detection, onvif, etc.
|
// goroutines which do image capture, motion detection, onvif, etc.
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// This will blocking until receiving a signal to be restarted, reconfigured, stopped, etc.
|
// This will blocking until receiving a signal to be restarted, reconfigured, stopped, etc.
|
||||||
status := RunAgent(configuration, communication, uptimeStart)
|
status := RunAgent(configuration, communication, uptimeStart, cameraSettings, decoder, subDecoder)
|
||||||
if status == "stop" {
|
if status == "stop" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -72,24 +79,27 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
|
|||||||
log.Log.Debug("Bootstrap: finished")
|
log.Log.Debug("Bootstrap: finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunAgent(configuration *models.Configuration, communication *models.Communication, uptimeStart time.Time) string {
|
func RunAgent(configuration *models.Configuration, communication *models.Communication, uptimeStart time.Time, cameraSettings *models.Camera, decoder *ffmpeg.VideoDecoder, subDecoder *ffmpeg.VideoDecoder) string {
|
||||||
log.Log.Debug("RunAgent: started")
|
log.Log.Debug("RunAgent: bootstrapping agent")
|
||||||
|
|
||||||
config := configuration.Config
|
config := configuration.Config
|
||||||
|
|
||||||
// Currently only support H264 encoded cameras, this will change.
|
// Currently only support H264 encoded cameras, this will change.
|
||||||
// Establishing the camera connection
|
// Establishing the camera connection
|
||||||
log.Log.Info("RunAgent: opening RTSP stream")
|
|
||||||
rtspUrl := config.Capture.IPCamera.RTSP
|
rtspUrl := config.Capture.IPCamera.RTSP
|
||||||
infile, streams, err := capture.OpenRTSP(rtspUrl)
|
infile, streams, err := capture.OpenRTSP(rtspUrl)
|
||||||
|
|
||||||
var queue *pubsub.Queue
|
var queue *pubsub.Queue
|
||||||
|
var subQueue *pubsub.Queue
|
||||||
|
|
||||||
|
var decoderMutex sync.Mutex
|
||||||
|
var subDecoderMutex sync.Mutex
|
||||||
|
|
||||||
status := "not started"
|
status := "not started"
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
||||||
log.Log.Info("RunAgent: opened RTSP stream")
|
log.Log.Info("RunAgent: opened RTSP stream" + rtspUrl)
|
||||||
|
|
||||||
// We might have a secondary rtsp url, so we might need to use that.
|
// We might have a secondary rtsp url, so we might need to use that.
|
||||||
var subInfile av.DemuxCloser
|
var subInfile av.DemuxCloser
|
||||||
@@ -99,20 +109,47 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
|||||||
if subRtspUrl != "" && subRtspUrl != rtspUrl {
|
if subRtspUrl != "" && subRtspUrl != rtspUrl {
|
||||||
subInfile, subStreams, err = capture.OpenRTSP(subRtspUrl)
|
subInfile, subStreams, err = capture.OpenRTSP(subRtspUrl)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Log.Info("RunAgent: opened RTSP sub stream")
|
log.Log.Info("RunAgent: opened RTSP sub stream " + subRtspUrl)
|
||||||
subStreamEnabled = true
|
subStreamEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At some routines we will need to decode the image.
|
// We will initialise the camera settings object
|
||||||
// Make sure its properly locked as we only have a single decoder.
|
// so we can check if the camera settings have changed, and we need
|
||||||
var decoderMutex sync.Mutex
|
// to reload the decoders.
|
||||||
var subDecoderMutex sync.Mutex
|
videoStream, _ := capture.GetVideoStream(streams)
|
||||||
decoder := capture.GetVideoDecoder(streams)
|
num, denum := videoStream.(av.VideoCodecData).Framerate()
|
||||||
|
width := videoStream.(av.VideoCodecData).Width()
|
||||||
|
height := videoStream.(av.VideoCodecData).Height()
|
||||||
|
|
||||||
var subDecoder *ffmpeg.VideoDecoder
|
if cameraSettings.RTSP != rtspUrl || cameraSettings.SubRTSP != subRtspUrl || cameraSettings.Width != width || cameraSettings.Height != height || cameraSettings.Num != num || cameraSettings.Denum != denum || cameraSettings.Codec != videoStream.(av.VideoCodecData).Type() {
|
||||||
if subStreamEnabled {
|
|
||||||
subDecoder = capture.GetVideoDecoder(subStreams)
|
if cameraSettings.Initialized {
|
||||||
|
decoder.Close()
|
||||||
|
if subStreamEnabled {
|
||||||
|
subDecoder.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At some routines we will need to decode the image.
|
||||||
|
// Make sure its properly locked as we only have a single decoder.
|
||||||
|
log.Log.Info("RunAgent: camera settings changed, reloading decoder")
|
||||||
|
capture.GetVideoDecoder(decoder, streams)
|
||||||
|
if subStreamEnabled {
|
||||||
|
capture.GetVideoDecoder(subDecoder, subStreams)
|
||||||
|
}
|
||||||
|
|
||||||
|
cameraSettings.RTSP = rtspUrl
|
||||||
|
cameraSettings.SubRTSP = subRtspUrl
|
||||||
|
cameraSettings.Width = width
|
||||||
|
cameraSettings.Height = height
|
||||||
|
cameraSettings.Framerate = float64(num) / float64(denum)
|
||||||
|
cameraSettings.Num = num
|
||||||
|
cameraSettings.Denum = denum
|
||||||
|
cameraSettings.Codec = videoStream.(av.VideoCodecData).Type()
|
||||||
|
cameraSettings.Initialized = true
|
||||||
|
} else {
|
||||||
|
log.Log.Info("RunAgent: camera settings did not change, keeping decoder")
|
||||||
}
|
}
|
||||||
|
|
||||||
communication.Decoder = decoder
|
communication.Decoder = decoder
|
||||||
@@ -136,10 +173,10 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
|||||||
queue.WriteHeader(streams)
|
queue.WriteHeader(streams)
|
||||||
|
|
||||||
// We might have a substream, if so we'll create a seperate queue.
|
// We might have a substream, if so we'll create a seperate queue.
|
||||||
var subQueue *pubsub.Queue
|
|
||||||
if subStreamEnabled {
|
if subStreamEnabled {
|
||||||
log.Log.Info("RunAgent: Creating sub stream queue with SetMaxGopCount set to " + strconv.Itoa(int(1)))
|
log.Log.Info("RunAgent: Creating sub stream queue with SetMaxGopCount set to " + strconv.Itoa(int(1)))
|
||||||
subQueue = pubsub.NewQueue()
|
subQueue = pubsub.NewQueue()
|
||||||
|
communication.SubQueue = subQueue
|
||||||
subQueue.SetMaxGopCount(1)
|
subQueue.SetMaxGopCount(1)
|
||||||
subQueue.WriteHeader(subStreams)
|
subQueue.WriteHeader(subStreams)
|
||||||
}
|
}
|
||||||
@@ -214,22 +251,30 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
|||||||
time.Sleep(time.Second * 1)
|
time.Sleep(time.Second * 1)
|
||||||
|
|
||||||
infile.Close()
|
infile.Close()
|
||||||
|
infile = nil
|
||||||
queue.Close()
|
queue.Close()
|
||||||
|
queue = nil
|
||||||
|
communication.Queue = nil
|
||||||
if subStreamEnabled {
|
if subStreamEnabled {
|
||||||
subInfile.Close()
|
subInfile.Close()
|
||||||
|
subInfile = nil
|
||||||
subQueue.Close()
|
subQueue.Close()
|
||||||
|
subQueue = nil
|
||||||
|
communication.SubQueue = nil
|
||||||
}
|
}
|
||||||
close(communication.HandleONVIF)
|
close(communication.HandleONVIF)
|
||||||
|
communication.HandleONVIF = nil
|
||||||
close(communication.HandleLiveHDHandshake)
|
close(communication.HandleLiveHDHandshake)
|
||||||
|
communication.HandleLiveHDHandshake = nil
|
||||||
close(communication.HandleMotion)
|
close(communication.HandleMotion)
|
||||||
routers.DisconnectMQTT(mqttClient)
|
communication.HandleMotion = nil
|
||||||
|
|
||||||
|
// Disconnect MQTT
|
||||||
|
routers.DisconnectMQTT(mqttClient, &configuration.Config)
|
||||||
|
|
||||||
// Wait a few seconds to stop the decoder.
|
// Wait a few seconds to stop the decoder.
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
decoder.Close()
|
|
||||||
if subStreamEnabled {
|
|
||||||
subDecoder.Close()
|
|
||||||
}
|
|
||||||
// Waiting for some seconds to make sure everything is properly closed.
|
// Waiting for some seconds to make sure everything is properly closed.
|
||||||
log.Log.Info("RunAgent: waiting 3 seconds to make sure everything is properly closed.")
|
log.Log.Info("RunAgent: waiting 3 seconds to make sure everything is properly closed.")
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
@@ -240,6 +285,9 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
|
|||||||
|
|
||||||
log.Log.Debug("RunAgent: finished")
|
log.Log.Debug("RunAgent: finished")
|
||||||
|
|
||||||
|
// Clean up, force garbage collection
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ func (s Stream) GetCodecs() []av.CodecData {
|
|||||||
|
|
||||||
func (s Stream) ReadPackets(packetChannel chan av.Packet) {
|
func (s Stream) ReadPackets(packetChannel chan av.Packet) {
|
||||||
session := s.Open()
|
session := s.Open()
|
||||||
fmt.Println("Start reading H264 packages from stream")
|
|
||||||
for {
|
for {
|
||||||
packet, err := session.ReadPacket()
|
packet, err := session.ReadPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ package computervision
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -130,48 +127,32 @@ func ProcessMotion(motionCursor *pubsub.QueueCursor, configuration *models.Confi
|
|||||||
|
|
||||||
// Store snapshots (jpg) for hull.
|
// Store snapshots (jpg) for hull.
|
||||||
if config.Capture.Snapshots != "false" {
|
if config.Capture.Snapshots != "false" {
|
||||||
files, err := ioutil.ReadDir("./data/snapshots")
|
StoreSnapshot(communication, frame, pkt, decoder, decoderMutex)
|
||||||
if err == nil {
|
|
||||||
rgbImage, err := GetRawImage(frame, pkt, decoder, decoderMutex)
|
|
||||||
if err == nil {
|
|
||||||
sort.Slice(files, func(i, j int) bool {
|
|
||||||
return files[i].ModTime().Before(files[j].ModTime())
|
|
||||||
})
|
|
||||||
if len(files) > 3 {
|
|
||||||
os.Remove("./data/snapshots/" + files[0].Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save image
|
|
||||||
t := strconv.FormatInt(time.Now().Unix(), 10)
|
|
||||||
f, err := os.Create("./data/snapshots/" + t + ".jpg")
|
|
||||||
if err == nil {
|
|
||||||
jpeg.Encode(f, &rgbImage.Image, &jpeg.Options{Quality: 15})
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if within time interval
|
// Check if within time interval
|
||||||
detectMotion := true
|
detectMotion := true
|
||||||
now := time.Now().In(loc)
|
timeEnabled := config.Time
|
||||||
weekday := now.Weekday()
|
if timeEnabled != "false" {
|
||||||
hour := now.Hour()
|
now := time.Now().In(loc)
|
||||||
minute := now.Minute()
|
weekday := now.Weekday()
|
||||||
second := now.Second()
|
hour := now.Hour()
|
||||||
timeInterval := config.Timetable[int(weekday)]
|
minute := now.Minute()
|
||||||
if timeInterval != nil {
|
second := now.Second()
|
||||||
start1 := timeInterval.Start1
|
timeInterval := config.Timetable[int(weekday)]
|
||||||
end1 := timeInterval.End1
|
if timeInterval != nil {
|
||||||
start2 := timeInterval.Start2
|
start1 := timeInterval.Start1
|
||||||
end2 := timeInterval.End2
|
end1 := timeInterval.End1
|
||||||
currentTimeInSeconds := hour*60*60 + minute*60 + second
|
start2 := timeInterval.Start2
|
||||||
if (currentTimeInSeconds >= start1 && currentTimeInSeconds <= end1) ||
|
end2 := timeInterval.End2
|
||||||
(currentTimeInSeconds >= start2 && currentTimeInSeconds <= end2) {
|
currentTimeInSeconds := hour*60*60 + minute*60 + second
|
||||||
|
if (currentTimeInSeconds >= start1 && currentTimeInSeconds <= end1) ||
|
||||||
|
(currentTimeInSeconds >= start2 && currentTimeInSeconds <= end2) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
detectMotion = false
|
detectMotion = false
|
||||||
log.Log.Debug("ProcessMotion: Time interval not valid, disabling motion detection.")
|
log.Log.Info("ProcessMotion: Time interval not valid, disabling motion detection.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,8 +162,15 @@ func ProcessMotion(motionCursor *pubsub.QueueCursor, configuration *models.Confi
|
|||||||
isPixelChangeThresholdReached, changesToReturn = FindMotion(imageArray, coordinatesToCheck, pixelThreshold)
|
isPixelChangeThresholdReached, changesToReturn = FindMotion(imageArray, coordinatesToCheck, pixelThreshold)
|
||||||
if detectMotion && isPixelChangeThresholdReached {
|
if detectMotion && isPixelChangeThresholdReached {
|
||||||
|
|
||||||
if mqttClient != nil {
|
// If offline mode is disabled, send a message to the hub
|
||||||
mqttClient.Publish("kerberos/"+key+"/device/"+config.Key+"/motion", 2, false, "motion")
|
if config.Offline == "false" {
|
||||||
|
if mqttClient != nil {
|
||||||
|
if key != "" {
|
||||||
|
mqttClient.Publish("kerberos/"+key+"/device/"+config.Key+"/motion", 2, false, "motion")
|
||||||
|
} else {
|
||||||
|
mqttClient.Publish("kerberos/device/"+config.Key+"/motion", 2, false, "motion")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Capture.Recording != "false" {
|
if config.Capture.Recording != "false" {
|
||||||
@@ -255,3 +243,16 @@ func AbsDiffBitwiseAndThreshold(img1 *image.Gray, img2 *image.Gray, img3 *image.
|
|||||||
}
|
}
|
||||||
return changes
|
return changes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StoreSnapshot(communication *models.Communication, frame *ffmpeg.VideoFrame, pkt av.Packet, decoder *ffmpeg.VideoDecoder, decoderMutex *sync.Mutex) {
|
||||||
|
rgbImage, err := GetRawImage(frame, pkt, decoder, decoderMutex)
|
||||||
|
if err == nil {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
w := bufio.NewWriter(buffer)
|
||||||
|
err := jpeg.Encode(w, &rgbImage.Image, &jpeg.Options{Quality: 15})
|
||||||
|
if err == nil {
|
||||||
|
snapshot := base64.StdEncoding.EncodeToString(buffer.Bytes())
|
||||||
|
communication.Image = snapshot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kerberos-io/agent/machinery/src/log"
|
||||||
"gopkg.in/mgo.v2"
|
"gopkg.in/mgo.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ func New() *mgo.Session {
|
|||||||
}
|
}
|
||||||
session, err := mgo.DialWithInfo(mongoDBDialInfo)
|
session, err := mgo.DialWithInfo(mongoDBDialInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error en mongo: %+v\n", err)
|
log.Log.Error(fmt.Sprintf("Failed to connect to database: %s", err.Error()))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
_instance.Session = session
|
_instance.Session = session
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/kerberos-io/joy4/av"
|
||||||
|
|
||||||
|
type Camera struct {
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
Num int
|
||||||
|
Denum int
|
||||||
|
Framerate float64
|
||||||
|
RTSP string
|
||||||
|
SubRTSP string
|
||||||
|
Codec av.CodecType
|
||||||
|
Initialized bool
|
||||||
|
}
|
||||||
@@ -28,8 +28,10 @@ type Communication struct {
|
|||||||
HandleONVIF chan OnvifAction
|
HandleONVIF chan OnvifAction
|
||||||
IsConfiguring *abool.AtomicBool
|
IsConfiguring *abool.AtomicBool
|
||||||
Queue *pubsub.Queue
|
Queue *pubsub.Queue
|
||||||
|
SubQueue *pubsub.Queue
|
||||||
DecoderMutex *sync.Mutex
|
DecoderMutex *sync.Mutex
|
||||||
SubDecoderMutex *sync.Mutex
|
SubDecoderMutex *sync.Mutex
|
||||||
Decoder *ffmpeg.VideoDecoder
|
Decoder *ffmpeg.VideoDecoder
|
||||||
SubDecoder *ffmpeg.VideoDecoder
|
SubDecoder *ffmpeg.VideoDecoder
|
||||||
|
Image string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,34 +12,35 @@ type Configuration struct {
|
|||||||
// Config is the highlevel struct which contains all the configuration of
|
// Config is the highlevel struct which contains all the configuration of
|
||||||
// your Kerberos Open Source instance.
|
// your Kerberos Open Source instance.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
FriendlyName string `json:"friendly_name"`
|
FriendlyName string `json:"friendly_name"`
|
||||||
Time string `json:"time" bson:"time"`
|
Time string `json:"time" bson:"time"`
|
||||||
Offline string `json:"offline"`
|
Offline string `json:"offline"`
|
||||||
AutoClean string `json:"auto_clean"`
|
AutoClean string `json:"auto_clean"`
|
||||||
MaxDirectorySize int64 `json:"max_directory_size"`
|
RemoveAfterUpload string `json:"remove_after_upload"`
|
||||||
Timezone string `json:"timezone,omitempty" bson:"timezone,omitempty"`
|
MaxDirectorySize int64 `json:"max_directory_size"`
|
||||||
Capture Capture `json:"capture"`
|
Timezone string `json:"timezone,omitempty" bson:"timezone,omitempty"`
|
||||||
Timetable []*Timetable `json:"timetable"`
|
Capture Capture `json:"capture"`
|
||||||
Region *Region `json:"region"`
|
Timetable []*Timetable `json:"timetable"`
|
||||||
Cloud string `json:"cloud" bson:"cloud"`
|
Region *Region `json:"region"`
|
||||||
S3 *S3 `json:"s3,omitempty" bson:"s3,omitempty"`
|
Cloud string `json:"cloud" bson:"cloud"`
|
||||||
KStorage *KStorage `json:"kstorage,omitempty" bson:"kstorage,omitempty"`
|
S3 *S3 `json:"s3,omitempty" bson:"s3,omitempty"`
|
||||||
MQTTURI string `json:"mqtturi" bson:"mqtturi,omitempty"`
|
KStorage *KStorage `json:"kstorage,omitempty" bson:"kstorage,omitempty"`
|
||||||
MQTTUsername string `json:"mqtt_username" bson:"mqtt_username"`
|
MQTTURI string `json:"mqtturi" bson:"mqtturi,omitempty"`
|
||||||
MQTTPassword string `json:"mqtt_password" bson:"mqtt_password"`
|
MQTTUsername string `json:"mqtt_username" bson:"mqtt_username"`
|
||||||
STUNURI string `json:"stunuri" bson:"stunuri"`
|
MQTTPassword string `json:"mqtt_password" bson:"mqtt_password"`
|
||||||
TURNURI string `json:"turnuri" bson:"turnuri"`
|
STUNURI string `json:"stunuri" bson:"stunuri"`
|
||||||
TURNUsername string `json:"turn_username" bson:"turn_username"`
|
TURNURI string `json:"turnuri" bson:"turnuri"`
|
||||||
TURNPassword string `json:"turn_password" bson:"turn_password"`
|
TURNUsername string `json:"turn_username" bson:"turn_username"`
|
||||||
HeartbeatURI string `json:"heartbeaturi" bson:"heartbeaturi"` /*obsolete*/
|
TURNPassword string `json:"turn_password" bson:"turn_password"`
|
||||||
HubURI string `json:"hub_uri" bson:"hub_uri"`
|
HeartbeatURI string `json:"heartbeaturi" bson:"heartbeaturi"` /*obsolete*/
|
||||||
HubKey string `json:"hub_key" bson:"hub_key"`
|
HubURI string `json:"hub_uri" bson:"hub_uri"`
|
||||||
HubPrivateKey string `json:"hub_private_key" bson:"hub_private_key"`
|
HubKey string `json:"hub_key" bson:"hub_key"`
|
||||||
HubSite string `json:"hub_site" bson:"hub_site"`
|
HubPrivateKey string `json:"hub_private_key" bson:"hub_private_key"`
|
||||||
ConditionURI string `json:"condition_uri" bson:"condition_uri"`
|
HubSite string `json:"hub_site" bson:"hub_site"`
|
||||||
|
ConditionURI string `json:"condition_uri" bson:"condition_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture defines which camera type (Id) you are using (IP, USB or Raspberry Pi camera),
|
// Capture defines which camera type (Id) you are using (IP, USB or Raspberry Pi camera),
|
||||||
@@ -50,10 +51,10 @@ type Capture struct {
|
|||||||
USBCamera USBCamera `json:"usbcamera"`
|
USBCamera USBCamera `json:"usbcamera"`
|
||||||
RaspiCamera RaspiCamera `json:"raspicamera"`
|
RaspiCamera RaspiCamera `json:"raspicamera"`
|
||||||
Recording string `json:"recording,omitempty"`
|
Recording string `json:"recording,omitempty"`
|
||||||
Continuous string `json:"continuous,omitempty"`
|
|
||||||
Snapshots string `json:"snapshots,omitempty"`
|
Snapshots string `json:"snapshots,omitempty"`
|
||||||
Motion string `json:"motion,omitempty"`
|
Motion string `json:"motion,omitempty"`
|
||||||
Liveview string `json:"liveview,omitempty"`
|
Liveview string `json:"liveview,omitempty"`
|
||||||
|
Continuous string `json:"continuous,omitempty"`
|
||||||
PostRecording int64 `json:"postrecording"`
|
PostRecording int64 `json:"postrecording"`
|
||||||
PreRecording int64 `json:"prerecording"`
|
PreRecording int64 `json:"prerecording"`
|
||||||
MaxLengthRecording int64 `json:"maxlengthrecording"`
|
MaxLengthRecording int64 `json:"maxlengthrecording"`
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
CPUId string `json:"cpu_idle" bson:"cpu_idle"`
|
CPUId string `json:"cpu_idle" bson:"cpu_idle"`
|
||||||
Hostname string `json:"hostname" bson:"hostname"`
|
Hostname string `json:"hostname" bson:"hostname"`
|
||||||
Version string `json:"version" bson:"version"`
|
Version string `json:"version" bson:"version"`
|
||||||
Release string `json:"release" bson:"release"`
|
Release string `json:"release" bson:"release"`
|
||||||
BootTime uint64 `json:"boot_time" bson:"boot_time"`
|
BootTime uint64 `json:"boot_time" bson:"boot_time"`
|
||||||
KernelVersion string `json:"kernel_version" bson:"kernel_version"`
|
KernelVersion string `json:"kernel_version" bson:"kernel_version"`
|
||||||
MACs []string `json:"macs" bson:"macs"`
|
MACs []string `json:"macs" bson:"macs"`
|
||||||
IPs []string `json:"ips" bson:"ips"`
|
IPs []string `json:"ips" bson:"ips"`
|
||||||
Architecture string `json:"architecture" bson:"architecture"`
|
Architecture string `json:"architecture" bson:"architecture"`
|
||||||
UsedMemory uint64 `json:"used_memory" bson:"used_memory"`
|
UsedMemory uint64 `json:"used_memory" bson:"used_memory"`
|
||||||
TotalMemory uint64 `json:"total_memory" bson:"total_memory"`
|
TotalMemory uint64 `json:"total_memory" bson:"total_memory"`
|
||||||
FreeMemory uint64 `json:"free_memory" bson:"free_memory"`
|
FreeMemory uint64 `json:"free_memory" bson:"free_memory"`
|
||||||
|
ProcessUsedMemory uint64 `json:"process_used_memory" bson:"process_used_memory"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
|||||||
"config": configuration.Config,
|
"config": configuration.Config,
|
||||||
"custom": configuration.CustomConfig,
|
"custom": configuration.CustomConfig,
|
||||||
"global": configuration.GlobalConfig,
|
"global": configuration.GlobalConfig,
|
||||||
"snapshot": components.GetSnapshot(),
|
"snapshot": communication.Image,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
|
|||||||
"config": configuration.Config,
|
"config": configuration.Config,
|
||||||
"custom": configuration.CustomConfig,
|
"custom": configuration.CustomConfig,
|
||||||
"global": configuration.GlobalConfig,
|
"global": configuration.GlobalConfig,
|
||||||
"snapshot": components.GetSnapshot(),
|
"snapshot": communication.Image,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -193,8 +193,16 @@ func MQTTListenerHandleONVIF(mqttClient mqtt.Client, hubKey string, configuratio
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisconnectMQTT(mqttClient mqtt.Client) {
|
func DisconnectMQTT(mqttClient mqtt.Client, config *models.Config) {
|
||||||
if mqttClient != nil {
|
if mqttClient != nil {
|
||||||
|
// Cleanup all subscriptions.
|
||||||
|
mqttClient.Unsubscribe("kerberos/" + config.HubKey + "/device/" + config.Key + "/request-live")
|
||||||
|
mqttClient.Unsubscribe(config.Key + "/register")
|
||||||
|
mqttClient.Unsubscribe("kerberos/webrtc/keepalivehub/" + config.Key)
|
||||||
|
mqttClient.Unsubscribe("kerberos/webrtc/peers/" + config.Key)
|
||||||
|
mqttClient.Unsubscribe("candidate/cloud")
|
||||||
|
mqttClient.Unsubscribe("kerberos/onvif/" + config.Key)
|
||||||
mqttClient.Disconnect(1000)
|
mqttClient.Disconnect(1000)
|
||||||
|
mqttClient = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package websocket
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ func WebsocketHandler(c *gin.Context, communication *models.Communication) {
|
|||||||
sockets[clientID].Cancels["stream-sd"]()
|
sockets[clientID].Cancels["stream-sd"]()
|
||||||
delete(sockets[clientID].Cancels, "stream-sd")
|
delete(sockets[clientID].Cancels, "stream-sd")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Streaming sd does not exists for " + clientID)
|
log.Log.Error("Streaming sd does not exists for " + clientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "stream-sd":
|
case "stream-sd":
|
||||||
@@ -101,7 +100,7 @@ func WebsocketHandler(c *gin.Context, communication *models.Communication) {
|
|||||||
|
|
||||||
_, exists := sockets[clientID].Cancels["stream-sd"]
|
_, exists := sockets[clientID].Cancels["stream-sd"]
|
||||||
if exists {
|
if exists {
|
||||||
fmt.Println("Already streaming sd for " + clientID)
|
log.Log.Info("Already streaming sd for " + clientID)
|
||||||
} else {
|
} else {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
sockets[clientID].Cancels["stream-sd"] = cancel
|
sockets[clientID].Cancels["stream-sd"] = cancel
|
||||||
@@ -136,7 +135,7 @@ func ForwardSDStream(ctx context.Context, clientID string, connection *Connectio
|
|||||||
logreader:
|
logreader:
|
||||||
for {
|
for {
|
||||||
var encodedImage string
|
var encodedImage string
|
||||||
if cursor != nil && decoder != nil {
|
if queue != nil && cursor != nil && decoder != nil {
|
||||||
pkt, err := cursor.ReadPacket()
|
pkt, err := cursor.ReadPacket()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if !pkt.IsKeyFrame {
|
if !pkt.IsKeyFrame {
|
||||||
|
|||||||
+63
-109
@@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -38,6 +39,17 @@ const (
|
|||||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func PrintASCIIArt() {
|
||||||
|
asciiArt := ` _ __ _ _
|
||||||
|
| |/ /___ _ __| |__ ___ _ __ ___ ___ (_) ___
|
||||||
|
| ' // _ \ '__| '_ \ / _ \ '__/ _ \/ __| | |/ _ \
|
||||||
|
| . \ __/ | | |_) | __/ | | (_) \__ \_| | (_) |
|
||||||
|
|_|\_\___|_| |_.__/ \___|_| \___/|___(_)_|\___/
|
||||||
|
|
||||||
|
`
|
||||||
|
fmt.Println(asciiArt)
|
||||||
|
}
|
||||||
|
|
||||||
func DirSize(path string) (int64, error) {
|
func DirSize(path string) (int64, error) {
|
||||||
var size int64
|
var size int64
|
||||||
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
||||||
@@ -102,26 +114,42 @@ func CheckDataDirectoryPermissions() error {
|
|||||||
recordingsDirectory := "./data/recordings"
|
recordingsDirectory := "./data/recordings"
|
||||||
configDirectory := "./data/config"
|
configDirectory := "./data/config"
|
||||||
snapshotsDirectory := "./data/snapshots"
|
snapshotsDirectory := "./data/snapshots"
|
||||||
|
cloudDirectory := "./data/cloud"
|
||||||
|
|
||||||
err := CheckDirectoryPermissions(recordingsDirectory)
|
err := CheckDirectoryPermissions(recordingsDirectory)
|
||||||
|
if err == nil {
|
||||||
|
err = CheckDirectoryPermissions(configDirectory)
|
||||||
|
if err == nil {
|
||||||
|
err = CheckDirectoryPermissions(snapshotsDirectory)
|
||||||
|
if err == nil {
|
||||||
|
err = CheckDirectoryPermissions(cloudDirectory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Log.Error("Checking data directory permissions: " + err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = CheckDirectoryPermissions(configDirectory)
|
|
||||||
if err != nil {
|
log.Log.Info("Checking data directory permissions: OK")
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = CheckDirectoryPermissions(snapshotsDirectory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckDirectoryPermissions(directory string) error {
|
func CheckDirectoryPermissions(directory string) error {
|
||||||
|
// Check if the directory exists
|
||||||
|
if _, err := os.Stat(directory); os.IsNotExist(err) {
|
||||||
|
return errors.New("Directory does not exist, " + directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create a file
|
||||||
file := directory + "/.test"
|
file := directory + "/.test"
|
||||||
f, err := os.Create(file)
|
f, err := os.Create(file)
|
||||||
defer f.Close()
|
if f != nil {
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will remove the file if it was created
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err := os.Remove(file)
|
err := os.Remove(file)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -136,7 +164,6 @@ func CheckDirectoryPermissions(directory string) error {
|
|||||||
func ReadDirectory(directory string) ([]os.FileInfo, error) {
|
func ReadDirectory(directory string) ([]os.FileInfo, error) {
|
||||||
ff, err := ioutil.ReadDir(directory)
|
ff, err := ioutil.ReadDir(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err.Error())
|
|
||||||
return []os.FileInfo{}, nil
|
return []os.FileInfo{}, nil
|
||||||
}
|
}
|
||||||
return ff, err
|
return ff, err
|
||||||
@@ -272,107 +299,34 @@ func CreateFragmentedMP4(fullName string, fragmentedDuration int64) {
|
|||||||
os.Rename(fullName+"f.mp4", fullName)
|
os.Rename(fullName+"f.mp4", fullName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func FloatToString(input_num float64) string {
|
func PrintEnvironmentVariables() {
|
||||||
// to convert a float number to a string
|
// Print environment variables that include "AGENT_" as a prefix.
|
||||||
return strconv.FormatFloat(input_num, 'f', 6, 64)
|
environmentVariables := ""
|
||||||
}
|
for _, e := range os.Environ() {
|
||||||
|
if strings.Contains(e, "AGENT_") {
|
||||||
func ParseFMP4(file *bytes.Reader) (error, uint64, uint64, []*mp4.MediaSegment, uint64) {
|
pair := strings.Split(e, "=")
|
||||||
var ftypSize, moovSize uint64
|
environmentVariables = environmentVariables + pair[0] + "=" + pair[1] + " "
|
||||||
var segments []*mp4.MediaSegment
|
|
||||||
var duration uint64
|
|
||||||
parsedMp4, err := mp4.DecodeFile(file, mp4.WithDecodeMode(mp4.DecModeLazyMdat))
|
|
||||||
if parsedMp4 != nil && parsedMp4.Init != nil && err == nil {
|
|
||||||
ftypSize = parsedMp4.Init.Ftyp.Size()
|
|
||||||
moovSize = parsedMp4.Init.Moov.Size()
|
|
||||||
duration = parsedMp4.Moov.Trak.Tkhd.Duration
|
|
||||||
segments = parsedMp4.Segments
|
|
||||||
}
|
|
||||||
return err, ftypSize, moovSize, segments, duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseFMP4Detail(fullName string) {
|
|
||||||
|
|
||||||
// open fullName
|
|
||||||
file, err := os.Open(fullName)
|
|
||||||
if err != nil {
|
|
||||||
log.Log.Error("Error opening file: " + err.Error())
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
fileBytes, err := ioutil.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Log.Error("Error reading file: " + err.Error())
|
|
||||||
}
|
|
||||||
fileReader := bytes.NewReader(fileBytes)
|
|
||||||
|
|
||||||
err, ftypSize, moovSize, segments, dur := ParseFMP4(fileReader)
|
|
||||||
fmt.Println("========== Fragmented details =================")
|
|
||||||
fmt.Println(dur)
|
|
||||||
fmt.Println(ftypSize)
|
|
||||||
fmt.Println(moovSize)
|
|
||||||
fmt.Println(len(segments))
|
|
||||||
|
|
||||||
// Calculate duration of segments
|
|
||||||
var totalDuration uint64
|
|
||||||
for _, segment := range segments {
|
|
||||||
fragments := segment.Fragments
|
|
||||||
|
|
||||||
for _, fragment := range fragments {
|
|
||||||
var sampleDuration uint32
|
|
||||||
samples := fragment.Moof.Traf.Trun.Samples
|
|
||||||
for _, sample := range samples {
|
|
||||||
sampleDuration += sample.Dur
|
|
||||||
}
|
|
||||||
fmt.Println(sampleDuration)
|
|
||||||
totalDuration += uint64(sampleDuration)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Log.Info("Printing out environmentVariables (AGENT_...): " + environmentVariables)
|
||||||
misingDuration := dur - totalDuration/100
|
|
||||||
fmt.Println(misingDuration)
|
|
||||||
fmt.Println(totalDuration/100 + misingDuration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateFragmentByteRanges(log log.Logging, fileName string, ftypSize uint64, moovSize uint64, segments []*mp4.MediaSegment) (string, []models.BytesRangeOnTime) {
|
func PrintConfiguration(configuration *models.Configuration) {
|
||||||
size := strconv.FormatInt(int64(ftypSize+moovSize), 10)
|
// We will print out the struct.
|
||||||
|
if configuration == nil {
|
||||||
var fileFragments strings.Builder
|
log.Log.Info("Configuration is nil")
|
||||||
fileFragments.WriteString("#EXT-X-MAP:URI=\"" + fileName + "\",BYTERANGE=\"" + size + "@0\"\n")
|
return
|
||||||
|
|
||||||
var bytesRangeOnTime []models.BytesRangeOnTime
|
|
||||||
var currentTime uint32
|
|
||||||
for _, segment := range segments {
|
|
||||||
fragments := segment.Fragments
|
|
||||||
for _, fragment := range fragments {
|
|
||||||
var sampleDuration uint32
|
|
||||||
samples := fragment.Moof.Traf.Trun.Samples
|
|
||||||
for _, sample := range samples {
|
|
||||||
sampleDuration += sample.Dur
|
|
||||||
}
|
|
||||||
currentTime = currentTime + sampleDuration
|
|
||||||
duration := FloatToString(float64(sampleDuration / 100000))
|
|
||||||
|
|
||||||
startPos := fragment.Moof.StartPos
|
|
||||||
start := strconv.FormatInt(int64(startPos), 10)
|
|
||||||
|
|
||||||
totalSize := fragment.Mdat.Size() + fragment.Moof.Size()
|
|
||||||
size := strconv.FormatInt(int64(totalSize), 10)
|
|
||||||
|
|
||||||
fileFragments.WriteString("#EXTINF:" + duration + ",\n") // @TODO calculate the duration
|
|
||||||
fileFragments.WriteString("#EXT-X-BYTERANGE:" + size + "@" + start + "\n")
|
|
||||||
fileFragments.WriteString(fileName + "\n")
|
|
||||||
|
|
||||||
byteRange := models.BytesRangeOnTime{
|
|
||||||
Duration: duration,
|
|
||||||
Time: FloatToString(float64(currentTime) / 100000),
|
|
||||||
Range: size + "@" + start,
|
|
||||||
}
|
|
||||||
bytesRangeOnTime = append(bytesRangeOnTime, byteRange)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
config := configuration.Config
|
||||||
bytesRanges := fileFragments.String()
|
// Iterate over the struct and printout the values.
|
||||||
log.Info("Fragments calculate from " + fileName + ": " + bytesRanges)
|
v := reflect.ValueOf(config)
|
||||||
return bytesRanges, bytesRangeOnTime
|
typeOfS := v.Type()
|
||||||
}*/
|
configurationVariables := ""
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
key := typeOfS.Field(i).Name
|
||||||
|
value := v.Field(i).Interface()
|
||||||
|
// Convert to string.
|
||||||
|
configurationVariables = configurationVariables + key + ": " + fmt.Sprintf("%v", value) + " "
|
||||||
|
}
|
||||||
|
log.Log.Info("Printing our configuration (config.json): " + configurationVariables)
|
||||||
|
}
|
||||||
|
|||||||
@@ -230,10 +230,7 @@ func WriteToTrack(livestreamCursor *pubsub.QueueCursor, configuration *models.Co
|
|||||||
// Later when we read a packet we need to figure out which track to send it to.
|
// Later when we read a packet we need to figure out which track to send it to.
|
||||||
videoIdx := -1
|
videoIdx := -1
|
||||||
audioIdx := -1
|
audioIdx := -1
|
||||||
log.Log.Info("WriteToTrack: listing codecs.")
|
|
||||||
for i, codec := range codecs {
|
for i, codec := range codecs {
|
||||||
log.Log.Info("WriteToTrack: codec - " + codec.Type().String() + " found.")
|
|
||||||
log.Log.Info(codec.Type().String())
|
|
||||||
if codec.Type().String() == "H264" && videoIdx < 0 {
|
if codec.Type().String() == "H264" && videoIdx < 0 {
|
||||||
videoIdx = i
|
videoIdx = i
|
||||||
} else if codec.Type().String() == "PCM_MULAW" && audioIdx < 0 {
|
} else if codec.Type().String() == "PCM_MULAW" && audioIdx < 0 {
|
||||||
@@ -367,7 +364,7 @@ func WriteToTrack(livestreamCursor *pubsub.QueueCursor, configuration *models.Co
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := track.WriteSample(sample); err != nil && err != io.ErrClosedPipe {
|
if err := track.WriteSample(sample); err != nil && err != io.ErrClosedPipe {
|
||||||
fmt.Println("WriteToTrack: something went wrong while writing sample: " + err.Error())
|
log.Log.Error("WriteToTrack: something went wrong while writing sample: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
"private": false,
|
"private": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@giantmachines/redux-websocket": "^1.5.1",
|
"@giantmachines/redux-websocket": "^1.5.1",
|
||||||
"@kerberos-io/ui": "1.70.0",
|
"@kerberos-io/ui": "^1.72.0",
|
||||||
"@material-ui/core": "^4.12.4",
|
"@material-ui/core": "^4.12.4",
|
||||||
"@material-ui/icons": "^4.11.3",
|
"@material-ui/icons": "^4.11.3",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
|
|||||||
@@ -198,7 +198,10 @@
|
|||||||
"kerberosvault_description_accesskey": "Der Zugriffsschlüssel des Kerberos Vault Accounts..",
|
"kerberosvault_description_accesskey": "Der Zugriffsschlüssel des Kerberos Vault Accounts..",
|
||||||
"kerberosvault_secretkey": "Geheimer Schlüssel",
|
"kerberosvault_secretkey": "Geheimer Schlüssel",
|
||||||
"kerberosvault_description_secretkey": "Der geheime Schlüssel des Kerberos Vault Accounts.",
|
"kerberosvault_description_secretkey": "Der geheime Schlüssel des Kerberos Vault Accounts.",
|
||||||
"verify_connection": "Verbindung prüfen"
|
"verify_connection": "Verbindung prüfen",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,10 @@
|
|||||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||||
"kerberosvault_secretkey": "Secret key",
|
"kerberosvault_secretkey": "Secret key",
|
||||||
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
||||||
"verify_connection": "Verify Connection"
|
"verify_connection": "Verify Connection",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,10 @@
|
|||||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||||
"kerberosvault_secretkey": "Secret key",
|
"kerberosvault_secretkey": "Secret key",
|
||||||
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
||||||
"verify_connection": "Verify Connection"
|
"verify_connection": "Verify Connection",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,10 @@
|
|||||||
"kerberosvault_description_accesskey": "La clé d'accès de votre compte Kerberos Vault.",
|
"kerberosvault_description_accesskey": "La clé d'accès de votre compte Kerberos Vault.",
|
||||||
"kerberosvault_secretkey": "Clé secrète",
|
"kerberosvault_secretkey": "Clé secrète",
|
||||||
"kerberosvault_description_secretkey": "La clé secrète de votre compte Kerberos Vault.",
|
"kerberosvault_description_secretkey": "La clé secrète de votre compte Kerberos Vault.",
|
||||||
"verify_connection": "Vérifier la connexion"
|
"verify_connection": "Vérifier la connexion",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
{
|
||||||
|
"breadcrumb": {
|
||||||
|
"watch_recordings": "録画を見る",
|
||||||
|
"configure": "設定"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"save": "保存"
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"profile": "プロフィール",
|
||||||
|
"admin": "管理者",
|
||||||
|
"management": "管理",
|
||||||
|
"dashboard": "ダッシュボード",
|
||||||
|
"recordings": "録画",
|
||||||
|
"settings": "設定",
|
||||||
|
"help_support": "ヘルプ",
|
||||||
|
"swagger": "Swagger API",
|
||||||
|
"documentation": "ドキュメンテーション",
|
||||||
|
"ui_library": "UI ライブラリ",
|
||||||
|
"layout": "言語",
|
||||||
|
"choose_language": "言語を選択"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"title": "ダッシュボード",
|
||||||
|
"heading": "ビデオ監視の概要",
|
||||||
|
"number_of_days": "日数",
|
||||||
|
"total_recordings": "録画一覧",
|
||||||
|
"connected": "接続済み",
|
||||||
|
"not_connected": "接続されていません",
|
||||||
|
"offline_mode": "オフラインモード",
|
||||||
|
"latest_events": "最新のイベント",
|
||||||
|
"configure_connection": "接続の構成",
|
||||||
|
"no_events": "イベントなし",
|
||||||
|
"no_events_description": "記録が見つかりません。Kerberos エージェントが正しく構成されていることを確認してください。",
|
||||||
|
"motion_detected": "モーションが検出されました",
|
||||||
|
"live_view": "ライブビュー",
|
||||||
|
"loading_live_view": "ライブビューを読み込んでいます",
|
||||||
|
"loading_live_view_description": "ライブ ビューをロードしています。お待ちください。",
|
||||||
|
"time": "時間",
|
||||||
|
"description": "説明",
|
||||||
|
"name": "名前"
|
||||||
|
},
|
||||||
|
"recordings": {
|
||||||
|
"title": "録画",
|
||||||
|
"heading": "すべての録音を 1 か所に",
|
||||||
|
"search_media": "メディアを検索"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "設定",
|
||||||
|
"heading": "搭載カメラ",
|
||||||
|
"submenu": {
|
||||||
|
"all": "全て",
|
||||||
|
"overview": "概要",
|
||||||
|
"camera": "カメラ",
|
||||||
|
"recording": "録音",
|
||||||
|
"streaming": "ストリーミング",
|
||||||
|
"conditions": "条件",
|
||||||
|
"persistence": "持続性"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"kerberos_hub_demo": "Kerberos Hub のデモ環境を見て、Kerberos Hub の動作を確認してください。",
|
||||||
|
"configuration_updated_success": "構成が正常に更新されました。",
|
||||||
|
"configuration_updated_error": "保存中にエラーが発生しました。",
|
||||||
|
"verify_hub": "Kerberos Hub の設定を確認しています。",
|
||||||
|
"verify_hub_success": "Kerberos Hub 設定が正常に検証されました。",
|
||||||
|
"verify_hub_error": "Kerberos Hub の検証中に問題が発生しました",
|
||||||
|
"verify_persistence": "持続性設定を確認しています。",
|
||||||
|
"verify_persistence_success": "持続性設定が正常に検証されました。",
|
||||||
|
"verify_persistence_error": "持続性の検証中に問題が発生しました",
|
||||||
|
"verify_camera": "カメラの設定を確認しています。",
|
||||||
|
"verify_camera_success": "カメラの設定が正常に検証されました。",
|
||||||
|
"verify_camera_error": "カメラ設定の確認中に問題が発生しました"
|
||||||
|
},
|
||||||
|
"overview": {
|
||||||
|
"general": "全般的",
|
||||||
|
"description_general": "Kerberos エージェントの一般設定",
|
||||||
|
"key": "鍵",
|
||||||
|
"camera_name": "カメラ名",
|
||||||
|
"timezone": "タイムゾーン",
|
||||||
|
"select_timezone": "タイムゾーンを選択",
|
||||||
|
"advanced_configuration": "詳細設定",
|
||||||
|
"description_advanced_configuration": "Kerberos エージェントの特定の部分を有効または無効にするための詳細な構成オプション",
|
||||||
|
"offline_mode": "オフラインモード",
|
||||||
|
"description_offline_mode": "すべての送信トラフィックを無効にする"
|
||||||
|
},
|
||||||
|
"camera": {
|
||||||
|
"camera": "カメラ",
|
||||||
|
"description_camera": "選択したカメラに接続するには、カメラの設定が必要です。",
|
||||||
|
"only_h264": "現在、H264 RTSP ストリームのみがサポートされています。",
|
||||||
|
"rtsp_url": "RTSP URL",
|
||||||
|
"rtsp_h264": "カメラへの H264 RTSP 接続。",
|
||||||
|
"sub_rtsp_url": "Sub RTSP url (ライブストリーミングに使用)",
|
||||||
|
"sub_rtsp_h264": "カメラの低解像度へのセカンダリ RTSP 接続。",
|
||||||
|
"onvif": "ONVIF",
|
||||||
|
"description_onvif": "ONVIF 機能と通信するための資格情報",
|
||||||
|
"onvif_xaddr": "ONVIF xaddr",
|
||||||
|
"onvif_username": "ONVIF ユーザー名",
|
||||||
|
"onvif_password": "ONVIF パスワード",
|
||||||
|
"verify_connection": "接続の確認",
|
||||||
|
"verify_sub_connection": "サブ接続の確認"
|
||||||
|
},
|
||||||
|
"recording": {
|
||||||
|
"recording": "録画",
|
||||||
|
"description_recording": "録画方法を指定します。",
|
||||||
|
"continuous_recording": "連続記録",
|
||||||
|
"description_continuous_recording": "24 時間またはモーション ベースの録画を行います。",
|
||||||
|
"max_duration": "動画の最大再生時間 (秒)",
|
||||||
|
"description_max_duration": "録音の最大継続時間。",
|
||||||
|
"pre_recording": "事前録画 (キー フレームのバッファリング)",
|
||||||
|
"description_pre_recording": "イベントが発生する数秒前。",
|
||||||
|
"post_recording": "ポストレコーディング (秒)",
|
||||||
|
"description_post_recording": "イベントが発生してからの秒数。",
|
||||||
|
"threshold": "記録閾値(ピクセル)",
|
||||||
|
"description_threshold": "記録するために変更されたピクセル数",
|
||||||
|
"autoclean": "オートクリーン",
|
||||||
|
"description_autoclean": "特定のストレージ容量 (MB) に達したときに、Kerberos エージェントが記録をクリーンアップできるかどうかを指定します。",
|
||||||
|
"autoclean_enable": "自動クリーニングを有効にする",
|
||||||
|
"autoclean_description_enable": "容量に達したら、最も古い記録を削除します。",
|
||||||
|
"autoclean_max_directory_size": "最大ディレクトリ サイズ (MB)",
|
||||||
|
"autoclean_description_max_directory_size": "保存された録音の最大 MB。",
|
||||||
|
"fragmentedrecordings": "断片化された録音",
|
||||||
|
"description_fragmentedrecordings": "録音が断片化されている場合、HLS ストリームに適しています。",
|
||||||
|
"fragmentedrecordings_enable": "断片化を有効にする",
|
||||||
|
"fragmentedrecordings_description_enable": "HLS には断片化された録音が必要です。",
|
||||||
|
"fragmentedrecordings_duration": "フラグメント期間",
|
||||||
|
"fragmentedrecordings_description_duration": "1 つのフラグメントの持続時間。"
|
||||||
|
},
|
||||||
|
"streaming": {
|
||||||
|
"stun_turn": "WebRTCのSTUN/TURN",
|
||||||
|
"description_stun_turn": "フル解像度のライブ ストリーミングには、WebRTC の概念を使用します。",
|
||||||
|
"stun_server": "STUNサーバー",
|
||||||
|
"turn_server": "TURNサーバー",
|
||||||
|
"turn_username": "ユーザー名",
|
||||||
|
"turn_password": "パスワード",
|
||||||
|
"stun_turn_forward": "転送とトランスコーディング",
|
||||||
|
"stun_turn_description_forward": "TURN/STUN 通信の最適化と機能強化。",
|
||||||
|
"stun_turn_webrtc": "WebRTC ブローカーへの転送",
|
||||||
|
"stun_turn_description_webrtc": "MQTT を介して h264 ストリームを転送する",
|
||||||
|
"stun_turn_transcode": "トランスコード ストリーム",
|
||||||
|
"stun_turn_description_transcode": "ストリームを低解像度に変換する",
|
||||||
|
"stun_turn_downscale": "解像度のダウンスケール (% または元の解像度)",
|
||||||
|
"mqtt": "MQTT",
|
||||||
|
"description_mqtt": "それらの通信にはMQTT ブローカーが使用されます。",
|
||||||
|
"description2_mqtt": "たとえば、ライブストリーミングや ONVIF (PTZ) 機能を実現するために、Kerberos エージェントに送信します。",
|
||||||
|
"mqtt_brokeruri": "ブローカー URI",
|
||||||
|
"mqtt_username": "ユーザー名",
|
||||||
|
"mqtt_password": "パスワード"
|
||||||
|
},
|
||||||
|
"conditions": {
|
||||||
|
"timeofinterest": "特定の時間",
|
||||||
|
"description_timeofinterest": "特定の時間間隔 (タイムゾーンに基づく) の間のみ録画を行います。",
|
||||||
|
"timeofinterest_enabled": "有効",
|
||||||
|
"timeofinterest_description_enabled": "有効にすると、時間枠を指定できます",
|
||||||
|
"sunday": "日曜日",
|
||||||
|
"monday": "月曜日",
|
||||||
|
"tuesday": "火曜日",
|
||||||
|
"wednesday": "水曜日",
|
||||||
|
"thursday": "木曜日",
|
||||||
|
"friday": "金曜日",
|
||||||
|
"saturday": "土曜日",
|
||||||
|
"externalcondition": "外部条件",
|
||||||
|
"description_externalcondition": "外部 Web サービスに応じて、記録を有効または無効にすることができます。",
|
||||||
|
"regionofinterest": "検出領域",
|
||||||
|
"description_regionofinterest": "1 つまたは複数の領域を定義すると、定義した領域でのみモーションが追跡されます。"
|
||||||
|
},
|
||||||
|
"persistence": {
|
||||||
|
"kerberoshub": "ケルベロス ハブ",
|
||||||
|
"description_kerberoshub": "Kerberos エージェントはハートビートを中央に送信できます。",
|
||||||
|
"description2_kerberoshub": "インストール。",
|
||||||
|
"persistence": "持続性",
|
||||||
|
"saasoffering": "Kerberos ハブ (SAAS オファリング)",
|
||||||
|
"description_persistence": "録音を保存する機能を持つことは、すべての始まりです。",
|
||||||
|
"description2_persistence": "、またはサードパーティのプロバイダ",
|
||||||
|
"select_persistence": "永続性を選択",
|
||||||
|
"kerberoshub_proxyurl": "Kerberos ハブ プロキシ URL",
|
||||||
|
"kerberoshub_description_proxyurl": "記録をアップロードするためのプロキシ エンドポイント。",
|
||||||
|
"kerberoshub_apiurl": "ケルベロス ハブ API URL",
|
||||||
|
"kerberoshub_description_apiurl": "録音をアップロードするための API エンドポイント。",
|
||||||
|
"kerberoshub_publickey": "公開鍵",
|
||||||
|
"kerberoshub_description_publickey": "Kerberos Hub アカウントに付与された公開鍵。",
|
||||||
|
"kerberoshub_privatekey": "秘密鍵",
|
||||||
|
"kerberoshub_description_privatekey": "Kerberos Hub アカウントに付与された秘密鍵。",
|
||||||
|
"kerberoshub_site": "サイト",
|
||||||
|
"kerberoshub_description_site": "Kerberos Hub で Kerberos エージェントが属しているサイト ID。",
|
||||||
|
"kerberoshub_region": "領域",
|
||||||
|
"kerberoshub_description_region": "録音を保存しているリージョン。",
|
||||||
|
"kerberoshub_bucket": "bucket",
|
||||||
|
"kerberoshub_description_bucket": "録音を保存しているbucket",
|
||||||
|
"kerberoshub_username": "ユーザー名/ディレクトリ",
|
||||||
|
"kerberoshub_description_username": "Kerberos Hub アカウントのユーザー名。",
|
||||||
|
"kerberosvault_apiurl": "Kerberos ボールト API URL",
|
||||||
|
"kerberosvault_description_apiurl": "Kerberos ボールト API",
|
||||||
|
"kerberosvault_provider": "プロバイダ",
|
||||||
|
"kerberosvault_description_provider": "録音の送信先のプロバイダー。",
|
||||||
|
"kerberosvault_directory": "ディレクトリ",
|
||||||
|
"kerberosvault_description_directory": "録音がプロバイダーに保存されるサブディレクトリ。",
|
||||||
|
"kerberosvault_accesskey": "アクセスキー",
|
||||||
|
"kerberosvault_description_accesskey": "Kerberos Vault アカウントのアクセス キー。",
|
||||||
|
"kerberosvault_secretkey": "秘密鍵",
|
||||||
|
"kerberosvault_description_secretkey": "Kerberos Vault アカウントの秘密鍵。",
|
||||||
|
"verify_connection": "接続の確認",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,7 +199,10 @@
|
|||||||
"kerberosvault_description_accesskey": "De access key van jouw Kerberos Vault account.",
|
"kerberosvault_description_accesskey": "De access key van jouw Kerberos Vault account.",
|
||||||
"kerberosvault_secretkey": "Secret key",
|
"kerberosvault_secretkey": "Secret key",
|
||||||
"kerberosvault_description_secretkey": "De secret key van jouw Kerberos Vault account.",
|
"kerberosvault_description_secretkey": "De secret key van jouw Kerberos Vault account.",
|
||||||
"verify_connection": "Verifieer verbinding"
|
"verify_connection": "Verifieer verbinding",
|
||||||
|
"remove_after_upload": "Wanneer opnames opgeslagen zijn, kan je ze verwijderen in de lokale Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Verwijder opnames wanneer ze zijn geupload naar een opslag locatie.",
|
||||||
|
"remove_after_upload_enabled": "Activeer verwijderen na uploaden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,10 @@
|
|||||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
||||||
"kerberosvault_secretkey": "Secret key",
|
"kerberosvault_secretkey": "Secret key",
|
||||||
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
||||||
"verify_connection": "Verify Connection"
|
"verify_connection": "Verify Connection",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
"watch_recordings": "Watch recordings",
|
"watch_recordings": "Assistir gravações",
|
||||||
"configure": "Configure"
|
"configure": "Configurar"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"save": "Save"
|
"save": "Salvar"
|
||||||
},
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"profile": "Perfil",
|
"profile": "Perfil",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"management": "Gestão",
|
"management": "Gestão",
|
||||||
"dashboard": "Painel",
|
"dashboard": "Painel",
|
||||||
"recordings": "Gravações",
|
"recordings": "Gravações",
|
||||||
"settings": "Definições",
|
"settings": "Configurações",
|
||||||
"help_support": "Ajuda e suporte",
|
"help_support": "Ajuda e suporte",
|
||||||
"swagger": "Swagger API",
|
"swagger": "Swagger API",
|
||||||
"documentation": "Documentação",
|
"documentation": "Documentação",
|
||||||
@@ -21,184 +21,187 @@
|
|||||||
"choose_language": "Escolha o seu idioma"
|
"choose_language": "Escolha o seu idioma"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "Dashboard",
|
"title": "Painel",
|
||||||
"heading": "Overview of your video surveilance",
|
"heading": "Visão geral de sua videovigilância",
|
||||||
"number_of_days": "Number of days",
|
"number_of_days": "Número de dias",
|
||||||
"total_recordings": "Total recordings",
|
"total_recordings": "Total de gravações",
|
||||||
"connected": "Connected",
|
"connected": "Conectado",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Desconectado",
|
||||||
"offline_mode": "Offline mode",
|
"offline_mode": "Modo Offline",
|
||||||
"latest_events": "Latest events",
|
"latest_events": "Ultimos eventos",
|
||||||
"configure_connection": "Configure connection",
|
"configure_connection": "Configurar conexão",
|
||||||
"no_events": "No events",
|
"no_events": "Nenhum evento",
|
||||||
"no_events_description": "No recordings where found, make sure your Kerberos Agent is properly configured.",
|
"no_events_description": "Nenhuma gravação foi encontrada, certifique-se de que seu Kerberos Agent esteja configurado corretamente.",
|
||||||
"motion_detected": "Motion was detected",
|
"motion_detected": "Movimento detectado",
|
||||||
"live_view": "Live view",
|
"live_view": "Visualização ao vivo",
|
||||||
"loading_live_view": "Loading live view",
|
"loading_live_view": "Carregando visualização ao vivo",
|
||||||
"loading_live_view_description": "Hold on we are loading your live view here. If you didn't configure your camera connection, update it on the settings pages.",
|
"loading_live_view_description": "Aguarde, estamos carregando sua visualização ao vivo. Se você não configurou a conexão de sua câmera, atualize-a nas páginas de configurações.",
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"description": "Description",
|
"description": "Descrição",
|
||||||
"name": "Name"
|
"name": "Nome"
|
||||||
},
|
},
|
||||||
"recordings": {
|
"recordings": {
|
||||||
"title": "Recordings",
|
"title": "Gravações",
|
||||||
"heading": "All your recordings in a single place",
|
"heading": "Todas as suas gravações em um único lugar",
|
||||||
"search_media": "Search media"
|
"search_media": "Pesquisar mídia"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Configurações",
|
||||||
"heading": "Onboard your camera",
|
"heading": "Integre sua câmera",
|
||||||
"submenu": {
|
"submenu": {
|
||||||
"all": "All",
|
"all": "Todas",
|
||||||
"overview": "Overview",
|
"overview": "Visão geral",
|
||||||
"camera": "Camera",
|
"camera": "Câmera",
|
||||||
"recording": "Recording",
|
"recording": "Gravação",
|
||||||
"streaming": "Streaming",
|
"streaming": "Transmissão",
|
||||||
"conditions": "Conditions",
|
"conditions": "Condições",
|
||||||
"persistence": "Persistence"
|
"persistence": "Armazenamento"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"kerberos_hub_demo": "Have a look at our Kerberos Hub demo environment, to see Kerberos Hub in action!",
|
"kerberos_hub_demo": "Dê uma olhada em nosso ambiente de demonstração do Kerberos Hub para ver o Kerberos Hub em ação!",
|
||||||
"configuration_updated_success": "Your configuration have been updated successfully.",
|
"configuration_updated_success": "Sua configuração foi atualizada com sucesso.",
|
||||||
"configuration_updated_error": "Something went wrong while saving.",
|
"configuration_updated_error": "Ocorreu um erro durante o salvamento.",
|
||||||
"verify_hub": "Verifying your Kerberos Hub settings.",
|
"verify_hub": "Verificando as configurações do Kerberos Hub.",
|
||||||
"verify_hub_success": "Kerberos Hub settings are successfully verified.",
|
"verify_hub_success": "As configurações do Kerberos Hub foram verificadas com sucesso.",
|
||||||
"verify_hub_error": "Something went wrong while verifying Kerberos Hub",
|
"verify_hub_error": "Algo deu errado ao verificar o Kerberos Hub",
|
||||||
"verify_persistence": "Verifying your persistence settings.",
|
"verify_persistence": "Verificando suas configurações de armazenamento.",
|
||||||
"verify_persistence_success": "Persistence settings are successfully verified.",
|
"verify_persistence_success": "As configurações de armazenamento foram verificadas com sucesso.",
|
||||||
"verify_persistence_error": "Something went wrong while verifying the persistence",
|
"verify_persistence_error": "Algo deu errado ao verificar o armazenamento",
|
||||||
"verify_camera": "Verifying your camera settings.",
|
"verify_camera": "Verificando as configurações de sua câmera.",
|
||||||
"verify_camera_success": "Camera settings are successfully verified.",
|
"verify_camera_success": "As configurações da câmera foram verificadas com sucesso.",
|
||||||
"verify_camera_error": "Something went wrong while verifying the camera settings"
|
"verify_camera_error": "Algo deu errado ao verificar as configurações da câmera."
|
||||||
},
|
},
|
||||||
"overview": {
|
"overview": {
|
||||||
"general": "General",
|
"general": "Geral",
|
||||||
"description_general": "General settings for your Kerberos Agent",
|
"description_general": "Configurações gerais para seu agente Kerberos",
|
||||||
"key": "Key",
|
"key": "Chave",
|
||||||
"camera_name": "Camera name",
|
"camera_name": "Nome da câmera",
|
||||||
"timezone": "Timezone",
|
"timezone": "Fuso horário",
|
||||||
"select_timezone": "Select a timezone",
|
"select_timezone": "Selecione a timezone",
|
||||||
"advanced_configuration": "Advanced configuration",
|
"advanced_configuration": "Configurações avançadas",
|
||||||
"description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent",
|
"description_advanced_configuration": "Opções de configuração detalhadas para habilitar ou desabilitar partes específicas do Kerberos Agent",
|
||||||
"offline_mode": "Offline mode",
|
"offline_mode": "Modo Offline",
|
||||||
"description_offline_mode": "Disable all outgoing traffic"
|
"description_offline_mode": "Desative todo o tráfego de saída"
|
||||||
},
|
},
|
||||||
"camera": {
|
"camera": {
|
||||||
"camera": "Camera",
|
"camera": "Câmera",
|
||||||
"description_camera": "Camera settings are required to make a connection to your camera of choice.",
|
"description_camera": "As configurações da câmera são necessárias para fazer uma conexão com a câmera de sua escolha.",
|
||||||
"only_h264": "Currently only H264 RTSP streams are supported.",
|
"only_h264": "Atualmente, apenas streams H264 RTSP são suportados.",
|
||||||
"rtsp_url": "RTSP url",
|
"rtsp_url": "Url RTSP",
|
||||||
"rtsp_h264": "A H264 RTSP connection to your camera.",
|
"rtsp_h264": "Uma conexão H264 RTSP para sua câmera.",
|
||||||
"sub_rtsp_url": "Sub RTSP url (used for livestreaming)",
|
"sub_rtsp_url": "Sub RTSP URL(usado para transmissão ao vivo)",
|
||||||
"sub_rtsp_h264": "A secondary RTSP connection to the low resolution of your camera.",
|
"sub_rtsp_h264": "Uma conexão RTSP secundária para a baixa resolução de sua câmera.",
|
||||||
"onvif": "ONVIF",
|
"onvif": "ONVIF",
|
||||||
"description_onvif": "Credentials to communicate with ONVIF capabilities. These are used for PTZ or other capabilities provided by the camera.",
|
"description_onvif": "Credenciais para se comunicar com recursos ONVIF. Eles são usados para PTZ ou outros recursos fornecidos pela câmera.",
|
||||||
"onvif_xaddr": "ONVIF xaddr",
|
"onvif_xaddr": "ONVIF xaddr",
|
||||||
"onvif_username": "ONVIF username",
|
"onvif_username": "ONVIF usuario (username)",
|
||||||
"onvif_password": "ONVIF password",
|
"onvif_password": "ONVIF senha (password)",
|
||||||
"verify_connection": "Verify Connection",
|
"verify_connection": "Verificar conexão",
|
||||||
"verify_sub_connection": "Verify Sub Connection"
|
"verify_sub_connection": "Verificar subconexão"
|
||||||
},
|
},
|
||||||
"recording": {
|
"recording": {
|
||||||
"recording": "Recording",
|
"recording": "Gravação",
|
||||||
"description_recording": "Specify how you would like to make recordings. Having a continuous 24/7 setup or a motion based recording.",
|
"description_recording": "Especifique como você gostaria de fazer gravações. Gravar o tempo todo (24/7) ou baseada na detecção de movimento.",
|
||||||
"continuous_recording": "Continuous recording",
|
"continuous_recording": "Gravação contínua",
|
||||||
"description_continuous_recording": "Make 24/7 or motion based recordings.",
|
"description_continuous_recording": "Faça gravações 24/7 ou baseadas na detecção de movimento.",
|
||||||
"max_duration": "max video duration (seconds)",
|
"max_duration": "duração máxima do vídeo (segundos)",
|
||||||
"description_max_duration": "The maximum duration of a recording.",
|
"description_max_duration": "A duração máxima de uma gravação.",
|
||||||
"pre_recording": "pre recording (key frames buffered)",
|
"pre_recording": "pré-gravação (key frames em buffer)",
|
||||||
"description_pre_recording": "Seconds before an event occurred.",
|
"description_pre_recording": "Segundos antes de um evento ocorrer.",
|
||||||
"post_recording": "post recording (seconds)",
|
"post_recording": "pós gravação (segundos)",
|
||||||
"description_post_recording": "Seconds after an event occurred.",
|
"description_post_recording": "Segundos após a ocorrência de um evento.",
|
||||||
"threshold": "Recording threshold (pixels)",
|
"threshold": "Limite de gravação (pixels)",
|
||||||
"description_threshold": "The number of pixels changed to record",
|
"description_threshold": "O número de pixels alterados para gravar",
|
||||||
"autoclean": "Auto clean",
|
"autoclean": "Limpeza automática",
|
||||||
"description_autoclean": "Specify if the Kerberos Agent can cleanup recordings when a specific storage capacity (MB) is reached. This will remove the oldest recordings when the capacity is reached.",
|
"description_autoclean": "Especifique se o Agente Kerberos pode limpar gravações quando uma capacidade de armazenamento específica (MB) é atingida. Isso removerá as gravações mais antigas quando a capacidade for atingida.",
|
||||||
"autoclean_enable": "Enable auto clean",
|
"autoclean_enable": "Ativar limpeza automática",
|
||||||
"autoclean_description_enable": "Remove oldest recording when capacity reached.",
|
"autoclean_description_enable": "Remova a gravação mais antiga quando a capacidade for atingida.",
|
||||||
"autoclean_max_directory_size": "Maximum directory size (MB)",
|
"autoclean_max_directory_size": "Tamanho máximo do diretório (MB)",
|
||||||
"autoclean_description_max_directory_size": "The maximum MB's of recordings stored.",
|
"autoclean_description_max_directory_size": "O máximo de MB de gravações armazenadas.",
|
||||||
"fragmentedrecordings": "Fragmented recordings",
|
"fragmentedrecordings": "Gravações Fragmentadas",
|
||||||
"description_fragmentedrecordings": "When recordings are fragmented they are suitable for an HLS stream. When turned on the MP4 container will look a bit different.",
|
"description_fragmentedrecordings": "Quando as gravações são fragmentadas, elas são adequadas para um fluxo HLS. Quando ativado, o contêiner MP4 parecerá um pouco diferente.",
|
||||||
"fragmentedrecordings_enable": "Enable fragmentation",
|
"fragmentedrecordings_enable": "Ativar fragmentação",
|
||||||
"fragmentedrecordings_description_enable": "Fragmented recordings are required for HLS.",
|
"fragmentedrecordings_description_enable": "Gravações fragmentadas são necessárias para HLS.",
|
||||||
"fragmentedrecordings_duration": "fragment duration",
|
"fragmentedrecordings_duration": "Duração do fragmento",
|
||||||
"fragmentedrecordings_description_duration": "Duration of a single fragment."
|
"fragmentedrecordings_description_duration": "Duração de um único fragmento."
|
||||||
},
|
},
|
||||||
"streaming": {
|
"streaming": {
|
||||||
"stun_turn": "STUN/TURN for WebRTC",
|
"stun_turn": "STUN/TURN para WebRTC",
|
||||||
"description_stun_turn": "For full-resolution livestreaming we use the concept of WebRTC. One of the key capabilities is the ICE-candidate feature, which allows NAT traversal using the concepts of STUN/TURN.",
|
"description_stun_turn": "Para transmissão ao vivo de resolução total, usamos o conceito de WebRTC. Um dos principais recursos é o recurso ICE-candidate, que permite a travessia de NAT usando os conceitos de STUN/TURN.",
|
||||||
"stun_server": "STUN server",
|
"stun_server": "Servidor STUN",
|
||||||
"turn_server": "TURN server",
|
"turn_server": "Servidor TURN",
|
||||||
"turn_username": "Username",
|
"turn_username": "Usuario",
|
||||||
"turn_password": "Password",
|
"turn_password": "Senha",
|
||||||
"stun_turn_forward": "Forwarding and transcoding",
|
"stun_turn_forward": "Encaminhamento e transcodificação",
|
||||||
"stun_turn_description_forward": "Optimisations and enhancements for TURN/STUN communication.",
|
"stun_turn_description_forward": "Otimizações e melhorias para a comunicação TURN/STUN.",
|
||||||
"stun_turn_webrtc": "Forwarding to WebRTC broker",
|
"stun_turn_webrtc": "Encaminhamento para broker WebRTC",
|
||||||
"stun_turn_description_webrtc": "Forward h264 stream through MQTT",
|
"stun_turn_description_webrtc": "Encaminhar stream h264 através do MQTT",
|
||||||
"stun_turn_transcode": "Transcode stream",
|
"stun_turn_transcode": "Transcodificar stream",
|
||||||
"stun_turn_description_transcode": "Convert stream to a lower resolution",
|
"stun_turn_description_transcode": "Converter stream para uma resolução mais baixa",
|
||||||
"stun_turn_downscale": "Downscale resolution (in % or original resolution)",
|
"stun_turn_downscale": "Resolução reduzida (em % ou resolução original)",
|
||||||
"mqtt": "MQTT",
|
"mqtt": "MQTT",
|
||||||
"description_mqtt": "A MQTT broker is used to communicate from",
|
"description_mqtt": "Um broker MQTT é usado para se comunicar de",
|
||||||
"description2_mqtt": "to the Kerberos Agent, to achieve for example livestreaming or ONVIF (PTZ) capabilities.",
|
"description2_mqtt": "para o Kerberos Agent, para obter, por exemplo, recursos de transmissão ao vivo ou ONVIF (PTZ).",
|
||||||
"mqtt_brokeruri": "Broker Uri",
|
"mqtt_brokeruri": "Url Broker",
|
||||||
"mqtt_username": "Username",
|
"mqtt_username": "Usuario",
|
||||||
"mqtt_password": "Password"
|
"mqtt_password": "Senha"
|
||||||
},
|
},
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"timeofinterest": "Time Of Interest",
|
"timeofinterest": "Tempo de interesse",
|
||||||
"description_timeofinterest": "Only make recordings between specific time intervals (based on timezone).",
|
"description_timeofinterest": "Apenas faça gravações entre intervalos de tempo específicos (com base no fuso horário).",
|
||||||
"timeofinterest_enabled": "Enabled",
|
"timeofinterest_enabled": "Habilitado",
|
||||||
"timeofinterest_description_enabled": "If enabled you can specify time windows",
|
"timeofinterest_description_enabled": "Se ativado, você pode especificar os intervalos de tempo",
|
||||||
"sunday": "Sunday",
|
"sunday": "Domingo",
|
||||||
"monday": "Monday",
|
"monday": "Segunda",
|
||||||
"tuesday": "Tuesday",
|
"tuesday": "Terça",
|
||||||
"wednesday": "Wednesday",
|
"wednesday": "Quarta",
|
||||||
"thursday": "Thursday",
|
"thursday": "Quinta",
|
||||||
"friday": "Friday",
|
"friday": "Sexta",
|
||||||
"saturday": "Saturday",
|
"saturday": "Sabado",
|
||||||
"externalcondition": "External Condition",
|
"externalcondition": "Condição Externa",
|
||||||
"description_externalcondition": "Depending on an external webservice recording can be enabled or disabled.",
|
"description_externalcondition": "Dependendo de um serviço de web(API) externo, a gravação pode ser habilitada ou desabilitada.",
|
||||||
"regionofinterest": "Region Of Interest",
|
"regionofinterest": "Região de interesse",
|
||||||
"description_regionofinterest": "By defining one or more regions, motion will be tracked only in the regions you have defined."
|
"description_regionofinterest": "Ao definir uma ou mais regiões, o movimento será rastreado apenas nas regiões definidas por você."
|
||||||
},
|
},
|
||||||
"persistence": {
|
"persistence": {
|
||||||
"kerberoshub": "Kerberos Hub",
|
"kerberoshub": "Kerberos Hub",
|
||||||
"description_kerberoshub": "Kerberos Agents can send heartbeats to a central",
|
"description_kerberoshub": "Agentes Kerberos podem enviar sinais constantes para a central",
|
||||||
"description2_kerberoshub": "installation. Heartbeats and other relevant information are synced to Kerberos Hub to show realtime information about your video landscape.",
|
"description2_kerberoshub": "instalação. Os sinais e outras informações relevantes são sincronizadas com o Kerberos Hub para mostrar informações em tempo real sobre seu cenário de vídeo.",
|
||||||
"persistence": "Persistence",
|
"persistence": "Armazenamento",
|
||||||
"saasoffering": "Kerberos Hub (SAAS offering)",
|
"saasoffering": "Kerberos Hub (oferta SAAS)",
|
||||||
"description_persistence": "Having the ability to store your recordings is the beginning of everything. You can choose between our",
|
"description_persistence": "Ter a capacidade de armazenar suas gravações é o começo de tudo. Você pode escolher entre nossos",
|
||||||
"description2_persistence": ", or a 3rd party provider",
|
"description2_persistence": ", ou um provedor terceirizado",
|
||||||
"select_persistence": "Select a persistence",
|
"select_persistence": "Selecione um provedor de armazenamento",
|
||||||
"kerberoshub_proxyurl": "Kerberos Hub Proxy URL",
|
"kerberoshub_proxyurl": "Url proxy para Kerberos Hub",
|
||||||
"kerberoshub_description_proxyurl": "The Proxy endpoint for uploading your recordings.",
|
"kerberoshub_description_proxyurl": "O endpoint Proxy para enviar suas gravações.",
|
||||||
"kerberoshub_apiurl": "Kerberos Hub API URL",
|
"kerberoshub_apiurl": "Url de API do Kerberos Hub",
|
||||||
"kerberoshub_description_apiurl": "The API endpoint for uploading your recordings.",
|
"kerberoshub_description_apiurl": "O endpoint da API para enviar suas gravações.",
|
||||||
"kerberoshub_publickey": "Public key",
|
"kerberoshub_publickey": "Chave pública (Public key)",
|
||||||
"kerberoshub_description_publickey": "The public key granted to your Kerberos Hub account.",
|
"kerberoshub_description_publickey": "A chave pública concedida à sua conta do Kerberos Hub.",
|
||||||
"kerberoshub_privatekey": "Private key",
|
"kerberoshub_privatekey": "Chave privada (Private key)",
|
||||||
"kerberoshub_description_privatekey": "The private key granted to your Kerberos Hub account.",
|
"kerberoshub_description_privatekey": "A chave privada concedida à sua conta do Kerberos Hub.",
|
||||||
"kerberoshub_site": "Site",
|
"kerberoshub_site": "Local",
|
||||||
"kerberoshub_description_site": "The site ID the Kerberos Agents are belonging to in Kerberos Hub.",
|
"kerberoshub_description_site": "O ID do local ao qual os Agentes Kerberos pertencem no Hub Kerberos.",
|
||||||
"kerberoshub_region": "Region",
|
"kerberoshub_region": "Região",
|
||||||
"kerberoshub_description_region": "The region we are storing our recordings in.",
|
"kerberoshub_description_region": "A região em que estamos armazenando nossas gravações.",
|
||||||
"kerberoshub_bucket": "Bucket",
|
"kerberoshub_bucket": "Bucket",
|
||||||
"kerberoshub_description_bucket": "The bucket we are storing our recordings in.",
|
"kerberoshub_description_bucket": "O bucket no qual estamos armazenando nossas gravações.",
|
||||||
"kerberoshub_username": "Username/Directory",
|
"kerberoshub_username": "Nome de usuário/diretório (Username/Directory)",
|
||||||
"kerberoshub_description_username": "The username of your Kerberos Hub account.",
|
"kerberoshub_description_username": "O nome de usuário da sua conta do Kerberos Hub.",
|
||||||
"kerberosvault_apiurl": "Kerberos Vault API URL",
|
"kerberosvault_apiurl": "Url da API do Kerberos Vault",
|
||||||
"kerberosvault_description_apiurl": "The Kerberos Vault API",
|
"kerberosvault_description_apiurl": "a API Kerberos Vault",
|
||||||
"kerberosvault_provider": "Provider",
|
"kerberosvault_provider": "Provedor",
|
||||||
"kerberosvault_description_provider": "The provider to which your recordings will be send.",
|
"kerberosvault_description_provider": "O provedor para o qual suas gravações serão enviadas.",
|
||||||
"kerberosvault_directory": "Directory",
|
"kerberosvault_directory": "Diretório",
|
||||||
"kerberosvault_description_directory": "Sub directory the recordings will be stored in your provider.",
|
"kerberosvault_description_directory": "Subdiretório as gravações serão armazenadas em seu provedor.",
|
||||||
"kerberosvault_accesskey": "Access key",
|
"kerberosvault_accesskey": "Chave de acesso(Access key)",
|
||||||
"kerberosvault_description_accesskey": "The access key of your Kerberos Vault account.",
|
"kerberosvault_description_accesskey": "A chave de acesso da sua conta do Kerberos Vault.",
|
||||||
"kerberosvault_secretkey": "Secret key",
|
"kerberosvault_secretkey": "Chave secreta(Secret key)",
|
||||||
"kerberosvault_description_secretkey": "The secret key of your Kerberos Vault account.",
|
"kerberosvault_description_secretkey": "A chave secreta da sua conta do Kerberos Vault.",
|
||||||
"verify_connection": "Verify Connection"
|
"verify_connection": "Verificar conexão",
|
||||||
|
"remove_after_upload": "Once recordings are uploaded to some persistence, you might want to remove them from the local Kerberos Agent.",
|
||||||
|
"remove_after_upload_description": "Remove recordings after they are uploaded successfully.",
|
||||||
|
"remove_after_upload_enabled": "Enabled delete on upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
@import "./app.variables";
|
@import "./app.variables";
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
row-gap: size(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding-top: size(7);
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.MuiPopover-root {
|
||||||
|
z-index: 99999999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.offline-mode, .cloud-not-installed {
|
.offline-mode, .cloud-not-installed {
|
||||||
background: var(--upper-gradient);
|
background: var(--upper-gradient);
|
||||||
|
|||||||
@@ -46,11 +46,3 @@ export function doCheckIfInstalled(onSuccess, onError) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* export function doAuth(onSuccess, onError) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doRefreshToken(onSuccess, onError) {
|
|
||||||
|
|
||||||
} */
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const LanguageSelect = () => {
|
|||||||
de: { label: 'Deutsch', dir: 'ltr', active: false },
|
de: { label: 'Deutsch', dir: 'ltr', active: false },
|
||||||
pt: { label: 'Português', dir: 'ltr', active: false },
|
pt: { label: 'Português', dir: 'ltr', active: false },
|
||||||
es: { label: 'Español', dir: 'ltr', active: false },
|
es: { label: 'Español', dir: 'ltr', active: false },
|
||||||
|
ja: { label: '日本', dir: 'rlt', active: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!languageMap[selected]) {
|
if (!languageMap[selected]) {
|
||||||
|
|||||||
+3
-1
@@ -7,14 +7,16 @@ const websocketprotocol = protocol === 'http:' ? 'ws:' : 'wss:';
|
|||||||
|
|
||||||
const dev = {
|
const dev = {
|
||||||
ENV: 'dev',
|
ENV: 'dev',
|
||||||
|
// Comment the below lines, when using codespaces or other special DNS names (which you can't control)
|
||||||
HOSTNAME: hostname,
|
HOSTNAME: hostname,
|
||||||
API_URL: `${protocol}//${hostname}:8080/api`,
|
API_URL: `${protocol}//${hostname}:8080/api`,
|
||||||
URL: `${protocol}//${hostname}:8080`,
|
URL: `${protocol}//${hostname}:8080`,
|
||||||
WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
|
WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
|
||||||
|
|
||||||
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
|
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
|
||||||
|
// HOSTNAME: externalHost,
|
||||||
// API_URL: `${protocol}//${externalHost}/api`,
|
// API_URL: `${protocol}//${externalHost}/api`,
|
||||||
// URL: `${protocol}//${externalHost}`,
|
// URL: `${protocol}//${externalHost}`,
|
||||||
|
// WS_URL: `${websocketprotocol}//${externalHost}/ws`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const prod = {
|
const prod = {
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ i18n
|
|||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
load: 'languageOnly',
|
load: 'languageOnly',
|
||||||
whitelist: ['de', 'en', 'nl', 'fr', 'pl', 'es', 'pt'],
|
whitelist: ['de', 'en', 'nl', 'fr', 'pl', 'es', 'pt', 'ja'],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|||||||
@@ -220,14 +220,21 @@ class Dashboard extends React.Component {
|
|||||||
id="cells1"
|
id="cells1"
|
||||||
bodycells={[
|
bodycells={[
|
||||||
<>
|
<>
|
||||||
<div className="time">
|
<div
|
||||||
|
className="time"
|
||||||
|
onClick={() =>
|
||||||
|
this.openModal(
|
||||||
|
`${config.URL}/file/${event.key}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Ellipse status="success" />{' '}
|
<Ellipse status="success" />{' '}
|
||||||
<p data-tip="10m and 5s ago">{event.time}</p>
|
<p data-tip="10m and 5s ago">{event.time}</p>
|
||||||
</div>
|
</div>
|
||||||
</>,
|
</>,
|
||||||
<>
|
<>
|
||||||
<p
|
<p
|
||||||
className="pointer"
|
className="pointer event-description"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
this.openModal(
|
this.openModal(
|
||||||
`${config.URL}/file/${event.key}`
|
`${config.URL}/file/${event.key}`
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import "../../app.variables";
|
||||||
|
|
||||||
#dashboard {
|
#dashboard {
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
@@ -15,6 +17,13 @@
|
|||||||
video {
|
video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.event-description {
|
||||||
|
display: none;
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -3,11 +3,8 @@ import PropTypes from 'prop-types';
|
|||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
VideoContainer,
|
|
||||||
VideoCard,
|
VideoCard,
|
||||||
ControlBar,
|
|
||||||
Button,
|
Button,
|
||||||
Input,
|
|
||||||
Modal,
|
Modal,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
@@ -105,17 +102,7 @@ class Media extends React.Component {
|
|||||||
</Link>
|
</Link>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
|
|
||||||
<ControlBar>
|
<div className="stats grid-container --four-columns">
|
||||||
<Input
|
|
||||||
iconleft="search"
|
|
||||||
onChange={() => {}}
|
|
||||||
placeholder={t('recordings.search_media')}
|
|
||||||
layout="controlbar"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</ControlBar>
|
|
||||||
|
|
||||||
<VideoContainer cols={4} isVideoWall={false}>
|
|
||||||
{events.map((event) => (
|
{events.map((event) => (
|
||||||
<div
|
<div
|
||||||
key={event.key}
|
key={event.key}
|
||||||
@@ -135,7 +122,7 @@ class Media extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</VideoContainer>
|
</div>
|
||||||
{open && (
|
{open && (
|
||||||
<Modal>
|
<Modal>
|
||||||
<ModalHeader
|
<ModalHeader
|
||||||
|
|||||||
@@ -1966,6 +1966,32 @@ class Settings extends React.Component {
|
|||||||
<h4>{t('settings.persistence.persistence')}</h4>
|
<h4>{t('settings.persistence.persistence')}</h4>
|
||||||
</BlockHeader>
|
</BlockHeader>
|
||||||
<BlockBody>
|
<BlockBody>
|
||||||
|
<p>{t('settings.persistence.remove_after_upload')}</p>
|
||||||
|
<div className="toggle-wrapper">
|
||||||
|
<Toggle
|
||||||
|
on={config.remove_after_upload === 'true'}
|
||||||
|
disabled={false}
|
||||||
|
onClick={(event) =>
|
||||||
|
this.onUpdateToggle(
|
||||||
|
'',
|
||||||
|
'remove_after_upload',
|
||||||
|
event,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
{t('settings.persistence.remove_after_upload_enabled')}
|
||||||
|
</span>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
'settings.persistence.remove_after_upload_description'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{t('settings.persistence.description_persistence')}{' '}
|
{t('settings.persistence.description_persistence')}{' '}
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.table-container table {
|
.table-container table {
|
||||||
border-spacing: 0 12px;
|
border-spacing: 0 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
+340
-11
@@ -174,7 +174,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.9"
|
"@babel/types" "^7.18.9"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6":
|
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
||||||
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
||||||
@@ -1056,6 +1056,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0":
|
||||||
|
version "7.21.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
|
||||||
|
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3":
|
"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3":
|
||||||
version "7.18.10"
|
version "7.18.10"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
|
||||||
@@ -1206,11 +1213,151 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
|
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
|
||||||
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
|
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
|
||||||
|
|
||||||
|
"@date-io/core@^2.15.0", "@date-io/core@^2.16.0":
|
||||||
|
version "2.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/core/-/core-2.16.0.tgz#7871bfc1d9bca9aa35ad444a239505589d0f22f6"
|
||||||
|
integrity sha512-DYmSzkr+jToahwWrsiRA2/pzMEtz9Bq1euJwoOuYwuwIYXnZFtHajY2E6a1VNVDc9jP8YUXK1BvnZH9mmT19Zg==
|
||||||
|
|
||||||
|
"@date-io/date-fns@^2.15.0":
|
||||||
|
version "2.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-2.16.0.tgz#bd5e09b6ecb47ee55e593fc3a87e7b2caaa3da40"
|
||||||
|
integrity sha512-bfm5FJjucqlrnQcXDVU5RD+nlGmL3iWgkHTq3uAZWVIuBu6dDmGa3m8a6zo2VQQpu8ambq9H22UyUpn7590joA==
|
||||||
|
dependencies:
|
||||||
|
"@date-io/core" "^2.16.0"
|
||||||
|
|
||||||
|
"@date-io/dayjs@^2.15.0":
|
||||||
|
version "2.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/dayjs/-/dayjs-2.16.0.tgz#0d2c254ad8db1306fdc4b8eda197cb53c9af89dc"
|
||||||
|
integrity sha512-y5qKyX2j/HG3zMvIxTobYZRGnd1FUW2olZLS0vTj7bEkBQkjd2RO7/FEwDY03Z1geVGlXKnzIATEVBVaGzV4Iw==
|
||||||
|
dependencies:
|
||||||
|
"@date-io/core" "^2.16.0"
|
||||||
|
|
||||||
|
"@date-io/luxon@^2.15.0":
|
||||||
|
version "2.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/luxon/-/luxon-2.16.1.tgz#b08786614cb58831c729a15807753011e4acb966"
|
||||||
|
integrity sha512-aeYp5K9PSHV28946pC+9UKUi/xMMYoaGelrpDibZSgHu2VWHXrr7zWLEr+pMPThSs5vt8Ei365PO+84pCm37WQ==
|
||||||
|
dependencies:
|
||||||
|
"@date-io/core" "^2.16.0"
|
||||||
|
|
||||||
|
"@date-io/moment@^2.15.0":
|
||||||
|
version "2.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-2.16.1.tgz#ec6e0daa486871e0e6412036c6f806842a0eeed4"
|
||||||
|
integrity sha512-JkxldQxUqZBfZtsaCcCMkm/dmytdyq5pS1RxshCQ4fHhsvP5A7gSqPD22QbVXMcJydi3d3v1Y8BQdUKEuGACZQ==
|
||||||
|
dependencies:
|
||||||
|
"@date-io/core" "^2.16.0"
|
||||||
|
|
||||||
|
"@emotion/babel-plugin@^11.10.6":
|
||||||
|
version "11.10.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead"
|
||||||
|
integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-module-imports" "^7.16.7"
|
||||||
|
"@babel/runtime" "^7.18.3"
|
||||||
|
"@emotion/hash" "^0.9.0"
|
||||||
|
"@emotion/memoize" "^0.8.0"
|
||||||
|
"@emotion/serialize" "^1.1.1"
|
||||||
|
babel-plugin-macros "^3.1.0"
|
||||||
|
convert-source-map "^1.5.0"
|
||||||
|
escape-string-regexp "^4.0.0"
|
||||||
|
find-root "^1.1.0"
|
||||||
|
source-map "^0.5.7"
|
||||||
|
stylis "4.1.3"
|
||||||
|
|
||||||
|
"@emotion/cache@^11.10.5":
|
||||||
|
version "11.10.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12"
|
||||||
|
integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/memoize" "^0.8.0"
|
||||||
|
"@emotion/sheet" "^1.2.1"
|
||||||
|
"@emotion/utils" "^1.2.0"
|
||||||
|
"@emotion/weak-memoize" "^0.3.0"
|
||||||
|
stylis "4.1.3"
|
||||||
|
|
||||||
"@emotion/hash@^0.8.0":
|
"@emotion/hash@^0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||||
|
|
||||||
|
"@emotion/hash@^0.9.0":
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
|
||||||
|
integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
|
||||||
|
|
||||||
|
"@emotion/is-prop-valid@^1.2.0":
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
|
||||||
|
integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/memoize" "^0.8.0"
|
||||||
|
|
||||||
|
"@emotion/memoize@^0.8.0":
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
|
||||||
|
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
|
||||||
|
|
||||||
|
"@emotion/react@^11.10.4":
|
||||||
|
version "11.10.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11"
|
||||||
|
integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.18.3"
|
||||||
|
"@emotion/babel-plugin" "^11.10.6"
|
||||||
|
"@emotion/cache" "^11.10.5"
|
||||||
|
"@emotion/serialize" "^1.1.1"
|
||||||
|
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
|
||||||
|
"@emotion/utils" "^1.2.0"
|
||||||
|
"@emotion/weak-memoize" "^0.3.0"
|
||||||
|
hoist-non-react-statics "^3.3.1"
|
||||||
|
|
||||||
|
"@emotion/serialize@^1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0"
|
||||||
|
integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/hash" "^0.9.0"
|
||||||
|
"@emotion/memoize" "^0.8.0"
|
||||||
|
"@emotion/unitless" "^0.8.0"
|
||||||
|
"@emotion/utils" "^1.2.0"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@emotion/sheet@^1.2.1":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
|
||||||
|
integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
|
||||||
|
|
||||||
|
"@emotion/styled@^11.10.4":
|
||||||
|
version "11.10.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.6.tgz#d886afdc51ef4d66c787ebde848f3cc8b117ebba"
|
||||||
|
integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.18.3"
|
||||||
|
"@emotion/babel-plugin" "^11.10.6"
|
||||||
|
"@emotion/is-prop-valid" "^1.2.0"
|
||||||
|
"@emotion/serialize" "^1.1.1"
|
||||||
|
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
|
||||||
|
"@emotion/utils" "^1.2.0"
|
||||||
|
|
||||||
|
"@emotion/unitless@^0.8.0":
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
|
||||||
|
integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
|
||||||
|
|
||||||
|
"@emotion/use-insertion-effect-with-fallbacks@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df"
|
||||||
|
integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==
|
||||||
|
|
||||||
|
"@emotion/utils@^1.2.0":
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
|
||||||
|
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
|
||||||
|
|
||||||
|
"@emotion/weak-memoize@^0.3.0":
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
||||||
|
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
||||||
|
|
||||||
"@eslint/eslintrc@^0.4.3":
|
"@eslint/eslintrc@^0.4.3":
|
||||||
version "0.4.3"
|
version "0.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
|
||||||
@@ -1568,14 +1715,20 @@
|
|||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
"@kerberos-io/ui@1.70.0":
|
"@kerberos-io/ui@^1.71.0":
|
||||||
version "1.70.0"
|
version "1.71.0"
|
||||||
resolved "https://registry.yarnpkg.com/@kerberos-io/ui/-/ui-1.70.0.tgz#377f42725cc98d02af43e133cf66f3c10852caf0"
|
resolved "https://registry.yarnpkg.com/@kerberos-io/ui/-/ui-1.71.0.tgz#06914c94e8b0982068d2099acf8158917a511bfc"
|
||||||
integrity sha512-Sq7gxNAXUFtjUNUpKBKogbvnJ03l/oxkf7943I0GpAzI0n+YdWNxlGpShB5HxsiarRsb+HphwZjAZZY+WupUwA==
|
integrity sha512-pHCTn/iQTcQEPoCK82eJHGRn6BgzW3wgV4C+mNqdKOtLTquxL+vh7molEgC66tl3DGf7HyjSNa8LuoxYbt9TEg==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@emotion/react" "^11.10.4"
|
||||||
|
"@emotion/styled" "^11.10.4"
|
||||||
|
"@mui/icons-material" "^5.10.3"
|
||||||
|
"@mui/material" "^5.10.3"
|
||||||
|
"@mui/x-date-pickers" "^5.0.0-beta.7"
|
||||||
"@svgr/webpack" "^5.5.0"
|
"@svgr/webpack" "^5.5.0"
|
||||||
"@types/react-router-dom" "^5.1.7"
|
"@types/react-router-dom" "^5.1.7"
|
||||||
md5 "^2.3.0"
|
md5 "^2.3.0"
|
||||||
|
moment "^2.29.4"
|
||||||
react "^17.0.2"
|
react "^17.0.2"
|
||||||
react-dom "^17.0.2"
|
react-dom "^17.0.2"
|
||||||
react-router-dom "^5.2.0"
|
react-router-dom "^5.2.0"
|
||||||
@@ -1664,6 +1817,117 @@
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^16.8.0 || ^17.0.0"
|
react-is "^16.8.0 || ^17.0.0"
|
||||||
|
|
||||||
|
"@mui/base@5.0.0-alpha.121":
|
||||||
|
version "5.0.0-alpha.121"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.121.tgz#24a238a32fc0386efcfefaf88d80712a0e00560c"
|
||||||
|
integrity sha512-8nJRY76UqlJV+q/Yzo0tgGfPWEOa+4N9rjO81fMmcJqP0I6m54hLDXsjvMg4tvelY5eKHXUK6Tb7en+GHfTqZA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@emotion/is-prop-valid" "^1.2.0"
|
||||||
|
"@mui/types" "^7.2.3"
|
||||||
|
"@mui/utils" "^5.11.13"
|
||||||
|
"@popperjs/core" "^2.11.6"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/core-downloads-tracker@^5.11.13":
|
||||||
|
version "5.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.13.tgz#0419a88ae98b3eaff2a633229d23ee04c8868be1"
|
||||||
|
integrity sha512-lx+GXBR9h/ApZsEP728tl0pyZyuajto+VnBgsoAzw1d5+CbmOo8ZWieKwVUGxZlPT1wMYNUYS5NtKzCli0xYjw==
|
||||||
|
|
||||||
|
"@mui/icons-material@^5.10.3":
|
||||||
|
version "5.11.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.11.11.tgz#d4e01bd405b0dac779f5e060586277f91f3acb6e"
|
||||||
|
integrity sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
|
||||||
|
"@mui/material@^5.10.3":
|
||||||
|
version "5.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.11.13.tgz#bbbdc74a78d4a0e6257a7bca3b8fa38ac8ad3915"
|
||||||
|
integrity sha512-2CnSj43F+159LbGmTLLQs5xbGYMiYlpTByQhP7c7cMX6opbScctBFE1PuyElpAmwW8Ag9ysfZH1d1MFAmJQkjg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@mui/base" "5.0.0-alpha.121"
|
||||||
|
"@mui/core-downloads-tracker" "^5.11.13"
|
||||||
|
"@mui/system" "^5.11.13"
|
||||||
|
"@mui/types" "^7.2.3"
|
||||||
|
"@mui/utils" "^5.11.13"
|
||||||
|
"@types/react-transition-group" "^4.4.5"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
csstype "^3.1.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
|
"@mui/private-theming@^5.11.13":
|
||||||
|
version "5.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.11.13.tgz#7841acc7e0d85e3aad223b1a0fad11be9349ef01"
|
||||||
|
integrity sha512-PJnYNKzW5LIx3R+Zsp6WZVPs6w5sEKJ7mgLNnUXuYB1zo5aX71FVLtV7geyPXRcaN2tsoRNK7h444ED0t7cIjA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@mui/utils" "^5.11.13"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/styled-engine@^5.11.11":
|
||||||
|
version "5.11.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.11.11.tgz#9084c331fdcff2210ec33adf37f34e94d67202e4"
|
||||||
|
integrity sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@emotion/cache" "^11.10.5"
|
||||||
|
csstype "^3.1.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/system@^5.11.13":
|
||||||
|
version "5.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.11.13.tgz#67941e401d2dde8ac6c078738dbecd193ed9074f"
|
||||||
|
integrity sha512-OWP0Alp6C8ufnGm9+CZcl3d+OoRXL2PnrRT5ohaMLxvGL9OfNcL2t4JOjMmA0k1UAGd6E/Ygbu5lEPrZSDlvCg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@mui/private-theming" "^5.11.13"
|
||||||
|
"@mui/styled-engine" "^5.11.11"
|
||||||
|
"@mui/types" "^7.2.3"
|
||||||
|
"@mui/utils" "^5.11.13"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
csstype "^3.1.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/types@^7.2.3":
|
||||||
|
version "7.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.3.tgz#06faae1c0e2f3a31c86af6f28b3a4a42143670b9"
|
||||||
|
integrity sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==
|
||||||
|
|
||||||
|
"@mui/utils@^5.10.3", "@mui/utils@^5.11.13":
|
||||||
|
version "5.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.11.13.tgz#8d7317f221e8973200764820fa7f2a622dbc7150"
|
||||||
|
integrity sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@types/prop-types" "^15.7.5"
|
||||||
|
"@types/react-is" "^16.7.1 || ^17.0.0"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/x-date-pickers@^5.0.0-beta.7":
|
||||||
|
version "5.0.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-5.0.20.tgz#7b4e5b5a214a8095937ba7d82bb82acd6f270d72"
|
||||||
|
integrity sha512-ERukSeHIoNLbI1C2XRhF9wRhqfsr+Q4B1SAw2ZlU7CWgcG8UBOxgqRKDEOVAIoSWL+DWT6GRuQjOKvj6UXZceA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.18.9"
|
||||||
|
"@date-io/core" "^2.15.0"
|
||||||
|
"@date-io/date-fns" "^2.15.0"
|
||||||
|
"@date-io/dayjs" "^2.15.0"
|
||||||
|
"@date-io/luxon" "^2.15.0"
|
||||||
|
"@date-io/moment" "^2.15.0"
|
||||||
|
"@mui/utils" "^5.10.3"
|
||||||
|
"@types/react-transition-group" "^4.4.5"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-transition-group "^4.4.5"
|
||||||
|
rifm "^0.12.1"
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||||
@@ -1700,6 +1964,11 @@
|
|||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
source-map "^0.7.3"
|
source-map "^0.7.3"
|
||||||
|
|
||||||
|
"@popperjs/core@^2.11.6":
|
||||||
|
version "2.11.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
|
||||||
|
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||||
|
|
||||||
"@rollup/plugin-babel@^5.2.0":
|
"@rollup/plugin-babel@^5.2.0":
|
||||||
version "5.3.1"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
|
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
|
||||||
@@ -2135,7 +2404,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc"
|
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc"
|
||||||
integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==
|
integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*", "@types/prop-types@^15.7.5":
|
||||||
version "15.7.5"
|
version "15.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
@@ -2155,6 +2424,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
||||||
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||||
|
|
||||||
|
"@types/react-is@^16.7.1 || ^17.0.0":
|
||||||
|
version "17.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
|
||||||
|
integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-redux@^7.1.20":
|
"@types/react-redux@^7.1.20":
|
||||||
version "7.1.24"
|
version "7.1.24"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
||||||
@@ -2182,7 +2458,7 @@
|
|||||||
"@types/history" "^4.7.11"
|
"@types/history" "^4.7.11"
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-transition-group@^4.2.0":
|
"@types/react-transition-group@^4.2.0", "@types/react-transition-group@^4.4.5":
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||||
integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
|
integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
|
||||||
@@ -3256,7 +3532,7 @@ cliui@^7.0.2:
|
|||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
wrap-ansi "^7.0.0"
|
wrap-ansi "^7.0.0"
|
||||||
|
|
||||||
clsx@^1.0.4:
|
clsx@^1.0.4, clsx@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||||
@@ -3416,6 +3692,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.1"
|
safe-buffer "~5.1.1"
|
||||||
|
|
||||||
|
convert-source-map@^1.5.0:
|
||||||
|
version "1.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||||
|
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
|
||||||
|
|
||||||
cookie-signature@1.0.6:
|
cookie-signature@1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||||
@@ -3715,6 +3996,11 @@ csstype@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
||||||
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
||||||
|
|
||||||
|
csstype@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
|
||||||
|
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
|
||||||
|
|
||||||
damerau-levenshtein@^1.0.8:
|
damerau-levenshtein@^1.0.8:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
||||||
@@ -4784,6 +5070,11 @@ find-cache-dir@^3.3.1:
|
|||||||
make-dir "^3.0.2"
|
make-dir "^3.0.2"
|
||||||
pkg-dir "^4.1.0"
|
pkg-dir "^4.1.0"
|
||||||
|
|
||||||
|
find-root@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||||
|
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
||||||
|
|
||||||
find-up@^3.0.0:
|
find-up@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||||
@@ -5132,7 +5423,7 @@ history@4.10.1, history@^4.9.0:
|
|||||||
tiny-warning "^1.0.0"
|
tiny-warning "^1.0.0"
|
||||||
value-equal "^1.0.1"
|
value-equal "^1.0.1"
|
||||||
|
|
||||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||||
@@ -6755,6 +7046,11 @@ mkdirp@~0.5.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
|
moment@^2.29.4:
|
||||||
|
version "2.29.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||||
|
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
@@ -8001,7 +8297,7 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||||
|
|
||||||
react-is@^18.0.0:
|
react-is@^18.0.0, react-is@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||||
@@ -8128,7 +8424,7 @@ react-tooltip@^4.2.21:
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
uuid "^7.0.3"
|
uuid "^7.0.3"
|
||||||
|
|
||||||
react-transition-group@^4.4.0:
|
react-transition-group@^4.4.0, react-transition-group@^4.4.5:
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||||
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
||||||
@@ -8388,6 +8684,11 @@ reusify@^1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
|
rifm@^0.12.1:
|
||||||
|
version "0.12.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.1.tgz#8fa77f45b7f1cda2a0068787ac821f0593967ac4"
|
||||||
|
integrity sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==
|
||||||
|
|
||||||
rimraf@^3.0.0, rimraf@^3.0.2:
|
rimraf@^3.0.0, rimraf@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||||
@@ -8657,6 +8958,11 @@ setprototypeof@1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||||
|
|
||||||
|
sha-1@0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/sha-1/-/sha-1-0.1.1.tgz#2a39304bf41bbab11dd9efb7474ec25b1a92c257"
|
||||||
|
integrity sha512-dexizf3hB7d4Jq6Cd0d/NYQiqgEqIfZIpuMfwPfvSb6h06DZKmHyUe55jYwpHC12R42wpqXO6ouhiBpRzIcD/g==
|
||||||
|
|
||||||
shebang-command@^2.0.0:
|
shebang-command@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||||
@@ -8783,6 +9089,11 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
|
source-map@^0.5.7:
|
||||||
|
version "0.5.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
|
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
||||||
|
|
||||||
source-map@^0.7.3:
|
source-map@^0.7.3:
|
||||||
version "0.7.4"
|
version "0.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||||
@@ -9006,6 +9317,11 @@ stylehacks@^5.1.0:
|
|||||||
browserslist "^4.16.6"
|
browserslist "^4.16.6"
|
||||||
postcss-selector-parser "^6.0.4"
|
postcss-selector-parser "^6.0.4"
|
||||||
|
|
||||||
|
stylis@4.1.3:
|
||||||
|
version "4.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
|
||||||
|
integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
@@ -9486,6 +9802,11 @@ utils-merge@1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||||
|
|
||||||
|
uuid@3.3.2:
|
||||||
|
version "3.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||||
|
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
|
||||||
|
|
||||||
uuid@^7.0.3:
|
uuid@^7.0.3:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||||
@@ -9496,6 +9817,14 @@ uuid@^8.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
|
uuidv4@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-2.0.0.tgz#3ec764288f9e9c4e40f8027ad309c2c528be2976"
|
||||||
|
integrity sha512-sAUlwUVepcVk6bwnaW/oi6LCwMdueako5QQzRr90ioAVVcms6p1mV0PaSxK8gyAC4CRvKddsk217uUpZUbKd2Q==
|
||||||
|
dependencies:
|
||||||
|
sha-1 "0.1.1"
|
||||||
|
uuid "3.3.2"
|
||||||
|
|
||||||
v8-compile-cache@^2.0.3:
|
v8-compile-cache@^2.0.3:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||||
|
|||||||
Reference in New Issue
Block a user