diff --git a/.gitignore b/.gitignore
index 101a285..5cbb075 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,11 @@
cameras.sql
+.DS_Store
.project
.settings
*.swp
node_modules
prep.config
+settings-nvr-local.js
target
ui-dist
+yarn-error.log
diff --git a/README.md b/README.md
index 65405fe..7a25ecd 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ make this possible:
* [License](LICENSE.txt) — GPLv3
* [Building and installing](guide/install.md)
+* [UI Development](guide/developing-ui.md)
* [Troubleshooting](guide/troubleshooting.md)
# Getting help and getting involved
diff --git a/guide/developing-ui.md b/guide/developing-ui.md
new file mode 100644
index 0000000..c9d5d67
--- /dev/null
+++ b/guide/developing-ui.md
@@ -0,0 +1,146 @@
+# Working on UI development
+
+The UI is presented from a single HTML page (index.html) and any number
+of Javascript files, css files, images, etc. These are "packed" together
+using [webpack](https://webpack.js.org).
+
+For ongoing development it is possible to have the UI running in a web
+browser using "hot loading". This means that as you make changes to source
+files, they will be detected, the webpack will be recompiled and generated
+and then the browser will be informed to reload things. In combination with
+the debugger built into modern browsers this makes for a reasonable process.
+
+For a production build, the same process is followed, except with different
+settings. In particular, no hot loading development server will be started
+and more effort is expended on packing and minimizing the components of
+the application as represented in the various "bundles". Read more about
+this in the webpack documentation.
+
+## Getting started
+
+Checkout the branch you want to work on and type
+
+ $ yarn start
+
+This will pack and prepare a development setup. By default the development
+server that serves up the web page(s) will listen on
+[http://localhost:3000](http://localhost:3000) so you can direct your browser
+there.
+
+Make any changes to the source code as you desire (look at existing code
+for examples and typical style), and the browser will hot-load your changes.
+Often times you will make mistakes. Anything from a coding error (for which
+you can use the browser's debugger), or compilation breaking Javascript errors.
+The latter will often be reported as errors during the webpack assembly
+process, but some will show up in the browser console, or both.
+
+## Control and location of settings
+
+Much of the settings needed to put the UI together, run webpack etc. is
+located in a series of files that contain webpack configuration. These
+files all live in the "webpack" subdirectory. We will not explain all
+of them here, as you should rarely need to understand them, let alone
+modify them.
+
+What is worth mentioning is that the `package.json` file is configured
+to use a different webpack configuration for development vs. production
+builds. Both configurations depend on a shared configuration common to
+both.
+
+There are also some settings that control aspects of the MoonFire UI
+behavior, such as window titles etc. These settings are found in the
+`settings-nvr.js` file in the project root directory. They should be
+pretty self explanatory.
+
+The webpack configuration for all webpack builds is able to load the
+values from `settings-nvr.js` and then, if the file exists, load those
+from `settings-nvr-local.js` and use them to add to the configuration,
+or replace. You can take advantage of this to add your own configuration
+values in the "local" file, which does not get checked in, but is used
+to affect development, and production builds.
+
+## Special considerations for API calls
+
+The UI code will make calls to the MoonFire NVR's server API, which is
+assumed to run on the same host as the MoonFire server. This makes sense
+because that server is also normally the one serving up the UI. For UI
+development, however this is not always convenient, or even useful.
+
+For one, you may be doing this development on a machine other than where
+the main MoonFire server is running. That can work, but of course that
+machine will not be responding the the API calls. If the UI does not
+gracefully handle API failure errors (it should but development on that
+is ongoing), it may break your UI code.
+
+Therefore, for practical purposes, you may want the API calls to go to
+a different server than where the localhost is. Rather than editing the
+`webpack/dev.conf.js` file to make this happen, you should use a different
+mechanism. The reason for not modifying this file (unless the change is
+needed by all), is that the file is under source control and thus should
+not reflect settings that are just for your personal use.
+
+The manner in which you can achieve using a different server for API
+calls is by telling the development server to use a "proxy" for
+certain urls. All API calls start with "/api", so we'll take advantage
+of that. Create a the file `settings-nvr-local.js` right next to the standard
+`settings-nvr.js`. The file should look something like this:
+
+ module.exports.settings = {
+ devServer: {
+ proxy: {
+ '/api': 'http://192.168.1.232:8080'
+ }
+ }
+ };
+
+This tells the development server to proxy all urls that it encounters
+requests for to the url specified. The way this is done is by taking the
+full URI component for the original request, and appending it to the string
+configured on the right side of the path to be rewritten above. In a standard
+MoonFire install, the server (and thus the API server as well), will be
+listening on port 8080 a the IP address where the server lives. So adjust
+the above for reach your own MoonFire instance where there is a server
+running with some real data behind it.
+
+### Issues with "older" MoonFire builds
+
+You might also have though to change the "/api" string in the source code
+to include the IP address of the MoonFire server. This would use the
+"right" (desirable) URLs, but the requests will fail due to a violation
+of the Cross-Origin Resource Sharing (CORS) protocol. If you really
+need to, you can add a configuration option to the MoonFire server
+by modifying its "service definition". We will not explain here how.
+
+## Changing development server configuration
+
+You can find our standard configuration for the development server inside
+the `webpacks/dev.conf.js` file. Using the technique outlined above you
+can change ports, ip addresses etc. One example where this may come in
+useful is that you may want to "test" your new API code, running on
+machine "A" (from a development server), proxying API requests to machine
+"B" (because it has real data), from a browser running on machine "C".
+
+The development server normally is configured to listing on port 3000
+on "localhost." (which would correspond to machine "A" in this example).
+However, you cannot use "localhost" on another machine to refer to "A".
+You may think that using the IP address of "A" works, but it doesn't
+because "localhost" lives at an IP address local to machine "A".
+
+To make this work you must tell the development server on host "A" to
+listen differently. You need to configure it to listen on IP address
+"0.0.0.0", which means "all available interfaces". Once that is in
+place you can use the IP address to reach "A" from "C". "A" will then
+send API requests on to "B", and present final UI using information
+from "A" and "B" to the browser on "C".
+
+Modify the local settings to something like this:
+
+ module.exports.settings = {
+ devServer: {
+ host: "0.0.0.0",
+ proxy: {
+ '/api': 'http://192.168.1.232:8080'
+ }
+ }
+ };
+
diff --git a/guide/easy-install.md b/guide/easy-install.md
new file mode 100644
index 0000000..9702357
--- /dev/null
+++ b/guide/easy-install.md
@@ -0,0 +1,145 @@
+# Installing Moonfire NVR using provided scripts
+
+There are no binary packages of Moonfire NVR available yet, so it must be built
+from source. This is made easy using a few scripts that will do the job for you
+unless you have very a different operating system. The scripts are written and
+tested under ubuntu and raspbian but should not be hard to modify if necessary.
+You'll start by downloading Moonfire if you have not already done so.
+
+## Downloading
+
+See the [github page](https://github.com/scottlamb/moonfire-nvr) (in case
+you're not reading this text there already). You can download the bleeding
+edge version from the command line via git:
+
+ $ git clone https://github.com/scottlamb/moonfire-nvr.git
+
+## Preparation steps for easy install
+
+There are a few things to prepare if you want a truly turnkey install, but
+they are both optional.
+
+### Dedicated media directory
+
+An optional, but strongly suggested, step is to setup a dedicated hard disk
+for recording video.
+Moonfire works best if the video samples are collected on a hard drive of
+sufficient capacity and separate from the root and main file systems. This
+is particularly important on Raspberry Pi based systems as the flash based
+main file systems have a limited lifetime and are way too small to hold
+any significant amount of video.
+If a dedicated hard drive is available, set up the mount point (in this
+example we'll use /media/nvr/samples):
+
+ $ sudo vim /etc/fstab
+ $ sudo mount /mount/media/samples
+
+In the fstab you would add a line similar to this:
+
+ /dev/disk/by-uuid/23d550bc-0e38-4825-acac-1cac8a7e091f /media/nvr ext4 defaults,noatime 0 1
+
+You'll have to lookup the correct uuid for your disk. One way to do that is
+to issue the following commands:
+
+ $ ls -l /dev/disk/by-uuid
+
+Locate the device where your disk will be mounted (or is mounted), for example
+`/dev/sda1`. Now lookup the filename linked to that from the output of the
+`ls` command. This is the uuid you need.
+
+The setup script (see below) will create the necessary sample file dir on the mounted
+hard disk.
+
+
+## Setting everything up
+
+Start by executing the setup script:
+
+ $ cd moonfire-nvr
+ $ scripts/setup-ubuntu.sh
+
+If this is the very first time you run this script, a file named `prep.config`
+will be created and the script will stop. This file is where you will set
+or change variables that describe the Moonfire installation you want. The
+initial execution will put default values in this value, but only for the
+most commonly changed variables. For a full list of variables, see below.
+
+Once you modify this file (if desired), you run the setup script again. This
+time it will use the values in the file and proceed with the setup.
+The script will download and install any pre-requisites. Watch carefully for
+error messages. It may be you have conflicting installations. If that is the
+case you must either resolve those first, or go the manual route.
+
+The script may be given the "-f" option. If you do, you are telling the script
+that you do not want any existing installation of ffmpeg to be overwritten with
+a newer one. This could be important to you. If you do use it, and the version
+you have installed is not compatible with Moonfire, you will be told through
+a message. If you have no ffmpeg installed, the option is effectively ignored
+and the necessary version of ffmpeg will be installed.
+
+The setup script should only need to be run once (after `prep.config` has been
+created), although if you do a re-install of Moonfire, in particular a much
+newer version, it is a good idea to run it again as requirements and pre-requisites
+may have changed. Running the script multiple times should not have any negative effects.
+
+*WARNING* It is quite possible that during the running of the setup script,
+in particular during the building of libavutil you will see several compiler
+warnings. This, while undesirable, is a direct result of the original
+developers not cleaning up the cause(s) of these warnings. They are, however,
+just warnings and will not affect correct functioning of Moonfire.
+
+Once the setup is complete, two steps remain: building and then installing.
+There is a script for each of these scenarios, but since generally you would
+want to install after a succesul build, the build script automatically invokes
+the install script, unless specifically told not to.
+
+## Building
+
+The build script is involved like this:
+
+ $ scripts/build.sh
+
+This script will perform all steps necessary to build a complete Moonfire
+setup. If there are no build errors, this script will then automatically
+invoke the install script (see below).
+
+There are two options you may pass to this script. The first is "-B" which
+means "build only". In other words, this will stop the automatic invocation
+of the install script. The other option available is "-t" and causes the
+script to ignore the results of any tests. In other words, even if tests
+fail, the build phase will be considered successful. This can occasionally
+be useful if you are doing development, and have temporarily broken one
+or more test, but want to proceed anyway.
+
+## Installing
+
+The install step is performed by the script that can be manually invoked
+like this:
+
+ $ scripts/install.sh
+
+This script will copy various files resulting from the build to the correct
+locations. It will also create a "service configuration" for systemctl that
+can be used to control Moonfire. This service configuration can be prevented
+by using the "-s" option to this script. It will also prevent the automatic
+start of this configuration.
+
+
+## Configuration variables
+
+Although not all listed in the default prep.config file, these are the
+available configuration variable and their defaults. In the most frequent
+scenarios you will probably only change SAMPLE_MEDIA_DIR to point
+to your mounted external disk (/media/nvr in the example above).
+
+ NVR_USER=moonfire-nvr
+ NVR_GROUP=$NVR_USER
+ NVR_PORT=8080
+ NVR_HOME_BASE=/var/lib
+ DB_NAME=db
+ DB_DIR=$NVR_HOME/$DB_NAME
+ SAMPLE_FILE_DIR=sample
+ SAMPLE_MEDIA_DIR=$NVR_HOME
+ SERVICE_NAME=moonfire-nvr
+ SERVICE_DESC="Moonfire NVR"
+ SERVICE_BIN=/usr/local/bin/$SERVICE_NAME
diff --git a/guide/install-manual.md b/guide/install-manual.md
new file mode 100644
index 0000000..f85803e
--- /dev/null
+++ b/guide/install-manual.md
@@ -0,0 +1,201 @@
+# Installing Moonfire NVR
+
+This document describes how to install Moonfire NVR on a Linux system.
+
+## Downloading
+
+See the [github page](https://github.com/scottlamb/moonfire-nvr) (in case
+you're not reading this text there already). You can download the bleeding
+edge version from the command line via git:
+
+ $ git clone https://github.com/scottlamb/moonfire-nvr.git
+
+## Building from source
+
+There are no binary packages of Moonfire NVR available yet, so it must be built
+from source.
+
+Moonfire NVR is written in the [Rust Programming
+Language](https://www.rust-lang.org/en-US/). In the long term, I expect this
+will result in a more secure, full-featured, easy-to-install software.
+
+You will need the following C libraries installed:
+
+* [ffmpeg](http://ffmpeg.org/) version 2.x or 3.x, including `libavutil`,
+ `libavcodec` (to inspect H.264 frames), and `libavformat` (to connect to RTSP
+ servers and write `.mp4` files).
+
+ Note ffmpeg library versions older than 55.1.101, along with all versions of
+ the competing project [libav](http://libav.org), don't support socket
+ timeouts for RTSP. For reliable reconnections on error, it's strongly
+ recommended to use ffmpeg library versions >= 55.1.101.
+
+* [SQLite3](https://www.sqlite.org/).
+
+* [`ncursesw`](https://www.gnu.org/software/ncurses/), the UTF-8 version of
+ the `ncurses` library.
+
+On recent Ubuntu or Raspbian, the following command will install
+all non-Rust dependencies:
+
+ $ sudo apt-get install \
+ build-essential \
+ libavcodec-dev \
+ libavformat-dev \
+ libavutil-dev \
+ libncurses5-dev \
+ libncursesw5-dev \
+ libsqlite3-dev \
+ libssl-dev \
+ pkgconf
+
+Next, you need Rust 1.17+ and Cargo. The easiest way to install them is by following
+the instructions at [rustup.rs](https://www.rustup.rs/).
+
+Finally, building the UI requires [yarn](https://yarnpkg.com/en/).
+
+You can continue to follow the build/install instructions below for a manual
+build and install, or alternatively you can run the prep script called `prep.sh`.
+
+ $ cd moonfire-nvr
+ $ ./prep.sh
+
+The script will take the following command line options, should you need them:
+
+* `-S`: Skip updating and installing dependencies through apt-get. This too can be
+ useful on repeated builds.
+
+You can edit variables at the start of the script to influence names and
+directories, but defaults should suffice in most cases. For details refer to
+the script itself. We will mention just one option, needed when you follow the
+suggestion to separate database and samples between flash storage and a hard disk.
+If you have the hard disk mounted on, lets say `/media/nvr`, and you want to
+store the video samples inside a directory named `samples` there, you would set:
+
+ SAMPLE_FILE_DIR=/media/nvr/samples
+
+The script will perform all necessary steps to leave you with a fully built,
+installed moonfire-nvr binary. The only thing
+you'll have to do manually is add your camera configuration(s) to the database.
+Alternatively, before running the script, you can create a file named `cameras.sql`
+in the same directory as the `prep.sh` script and it will be automatically
+included for you.
+For instructions, you can skip to "[Camera configuration and hard disk mounting](#camera)".
+
+Once prerequisites are installed, Moonfire NVR can be built as follows:
+
+ $ yarn
+ $ yarn build
+ $ cargo test
+ $ cargo build --release
+ $ sudo install -m 755 target/release/moonfire-nvr /usr/local/bin
+ $ sudo mkdir /usr/local/lib/moonfire-nvr
+ $ sudo cp -R ui-dist /usr/local/lib/moonfire-nvr/ui
+
+## Further configuration
+
+Moonfire NVR should be run under a dedicated user. It keeps two kinds of
+state:
+
+ * a SQLite database, typically <1 GiB. It should be stored on flash if
+ available.
+ * the "sample file directory", which holds the actual samples/frames of
+ H.264 video. This should be quite large and typically is stored on a hard
+ drive.
+
+(See [schema.md](schema.md) for more information.)
+
+Both kinds of state are intended to be accessed only by Moonfire NVR itself.
+However, the interface for adding new cameras is not yet written, so you will
+have to manually insert cameras with the `sqlite3` command line tool prior to
+starting Moonfire NVR.
+
+Manual commands would look something like this:
+
+ $ sudo addgroup --system moonfire-nvr
+ $ sudo adduser --system moonfire-nvr --home /var/lib/moonfire-nvr
+ $ sudo mkdir /var/lib/moonfire-nvr
+ $ sudo -u moonfire-nvr -H mkdir db sample
+ $ sudo -u moonfire-nvr moonfire-nvr init
+
+### Camera configuration and hard drive mounting
+
+If a dedicated hard drive is available, set up the mount point:
+
+ $ sudo vim /etc/fstab
+ $ sudo mount /var/lib/moonfire-nvr/sample
+
+Once setup is complete, it is time to add camera configurations to the
+database. If the daemon is running, you will need to stop it temporarily:
+
+ $ sudo systemctl stop moonfire-nvr
+
+You can configure the system through a text-based user interface:
+
+ $ sudo -u moonfire-nvr moonfire-nvr config 2>debug-log
+
+In the user interface, add your cameras under the "Edit cameras" dialog.
+There's a "Test" button to verify your settings directly from the dialog.
+
+After the cameras look correct, go to "Edit retention" to assign disk space to
+each camera. Leave a little slack (at least 100 MB per camera) between the total
+limit and the filesystem capacity, even if you store nothing else on the disk.
+There are several reasons this is needed:
+
+ * The limit currently controls fully-written files only. There will be up
+ to two minutes of video per camera of additional video.
+ * The rotation happens after the limit is exceeded, not proactively.
+ * Moonfire NVR currently doesn't account for the unused space in the final
+ filesystem block at the end of each file.
+ * Moonfire NVR doesn't account for the space used for directory listings.
+ * If a file is open when it is deleted (such as if a HTTP client is
+ downloading it), it stays around until the file is closed. Moonfire NVR
+ currently doesn't account for this.
+
+When finished, start the daemon:
+
+ $ sudo systemctl start moonfire-nvr
+
+### System Service
+
+Moonfire NVR can be run as a systemd service. If you used `prep.sh` this has
+been done for you. If not, Create
+`/etc/systemd/system/moonfire-nvr.service`:
+
+ [Unit]
+ Description=Moonfire NVR
+ After=network-online.target
+
+ [Service]
+ ExecStart=/usr/local/bin/moonfire-nvr run \
+ --db-dir=/var/lib/moonfire-nvr/db \
+ --http-addr=0.0.0.0:8080
+ Environment=TZ=:/etc/localtime
+ Environment=MOONFIRE_FORMAT=google-systemd
+ Environment=MOONFIRE_LOG=info
+ Environment=RUST_BACKTRACE=1
+ Type=simple
+ User=moonfire-nvr
+ Nice=-20
+ Restart=on-abnormal
+ CPUAccounting=true
+ MemoryAccounting=true
+ BlockIOAccounting=true
+
+ [Install]
+ WantedBy=multi-user.target
+
+Note that the HTTP port currently has no authentication, encryption, or
+logging; it should not be directly exposed to the Internet.
+
+Complete the installation through `systemctl` commands:
+
+ $ sudo systemctl daemon-reload
+ $ sudo systemctl start moonfire-nvr
+ $ sudo systemctl status moonfire-nvr
+ $ sudo systemctl enable moonfire-nvr
+
+See the [systemd](http://www.freedesktop.org/wiki/Software/systemd/)
+documentation for more information. The [manual
+pages](http://www.freedesktop.org/software/systemd/man/) for `systemd.service`
+and `systemctl` may be of particular interest.
diff --git a/guide/install.md b/guide/install.md
index 290dae3..23c204c 100644
--- a/guide/install.md
+++ b/guide/install.md
@@ -13,84 +13,17 @@ edge version from the command line via git:
## Building from source
There are no binary packages of Moonfire NVR available yet, so it must be built
-from source.
+from source. To do so, you can follow two paths:
-Moonfire NVR is written in the [Rust Programming
-Language](https://www.rust-lang.org/en-US/). In the long term, I expect this
-will result in a more secure, full-featured, easy-to-install software.
+* Scripted install: You will run some shell scripts (after preparing one or
+ two files, and will be completely done. This is by far the easiest option,
+ in particular for first time builders/installers. This process is fully
+ described. Read more in [Easy Installation](easy-install.md)
+* Manual build and install. This is explained in detail in these
+ [instructions](install-manual.md)
-You will need the following C libraries installed:
-
-* [ffmpeg](http://ffmpeg.org/) version 2.x or 3.x, including `libavutil`,
- `libavcodec` (to inspect H.264 frames), and `libavformat` (to connect to RTSP
- servers and write `.mp4` files).
-
- Note ffmpeg library versions older than 55.1.101, along with all versions of
- the competing project [libav](http://libav.org), don't not support socket
- timeouts for RTSP. For reliable reconnections on error, it's strongly
- recommended to use ffmpeg library versions >= 55.1.101.
-
-* [SQLite3](https://www.sqlite.org/).
-
-* [`ncursesw`](https://www.gnu.org/software/ncurses/), the UTF-8 version of
- the `ncurses` library.
-
-On recent Ubuntu or Raspbian, the following command will install
-all non-Rust dependencies:
-
- $ sudo apt-get install \
- build-essential \
- libavcodec-dev \
- libavformat-dev \
- libavutil-dev \
- libncurses5-dev \
- libncursesw5-dev \
- libsqlite3-dev \
- libssl-dev \
- pkgconf
-
-Next, you need Rust 1.17+ and Cargo. The easiest way to install them is by following
-the instructions at [rustup.rs](https://www.rustup.rs/).
-
-Finally, building the UI requires [yarn](https://yarnpkg.com/en/).
-
-You can continue to follow the build/install instructions below for a manual
-build and install, or alternatively you can run the prep script called `prep.sh`.
-
- $ cd moonfire-nvr
- $ ./prep.sh
-
-The script will take the following command line options, should you need them:
-
-* `-S`: Skip updating and installing dependencies through apt-get. This too can be
- useful on repeated builds.
-
-You can edit variables at the start of the script to influence names and
-directories, but defaults should suffice in most cases. For details refer to
-the script itself. We will mention just one option, needed when you follow the
-suggestion to separate database and samples between flash storage and a hard disk.
-If you have the hard disk mounted on, lets say `/media/nvr`, and you want to
-store the video samples inside a directory named `samples` there, you would set:
-
- SAMPLES_DIR=/media/nvr/samples
-
-The script will perform all necessary steps to leave you with a fully built,
-installed moonfire-nvr binary. The only thing
-you'll have to do manually is add your camera configuration(s) to the database.
-Alternatively, before running the script, you can create a file named `cameras.sql`
-in the same directory as the `prep.sh` script and it will be automatically
-included for you.
-For instructions, you can skip to "[Camera configuration and hard disk mounting](#camera)".
-
-Once prerequisites are installed, Moonfire NVR can be built as follows:
-
- $ yarn
- $ yarn build
- $ cargo test
- $ cargo build --release
- $ sudo install -m 755 target/release/moonfire-nvr /usr/local/bin
- $ sudo mkdir /usr/local/lib/moonfire-nvr
- $ sudo cp -R ui-dist /usr/local/lib/moonfire-nvr/ui
+Regardless of the approach for setup and installation above that you choose,
+please read the further configuration instructions below.
## Further configuration
@@ -103,13 +36,13 @@ state:
H.264 video. These should be quite large and are typically stored on hard
drives.
+Both states are intended to be accessed by moonfire-nvr only, but can be
+changed after building. See below.
(See [schema.md](schema.md) for more information.)
-Both kinds of state are intended to be accessed only by Moonfire NVR itself.
-However, the interface for adding new cameras is not yet written, so you will
-have to manually insert cameras with the `sqlite3` command line tool prior to
-starting Moonfire NVR.
-
+The database changes and sample file directory changes require the moonfire-nvr
+binary to be built, so can only be done after completing the build. The other
+settings and preparations should be done before building.
Manual commands would look something like this:
$ sudo addgroup --system moonfire-nvr
@@ -118,7 +51,7 @@ Manual commands would look something like this:
$ sudo -u moonfire-nvr -H mkdir db sample
$ sudo -u moonfire-nvr moonfire-nvr init
-### Camera configuration and hard drive mounting
+### Camera configuration and hard drive mounting
If a dedicated hard drive is available, set up the mount point:
@@ -130,7 +63,7 @@ database. If the daemon is running, you will need to stop it temporarily:
$ sudo systemctl stop moonfire-nvr
-You can configure the system through a text-based user interface:
+You can configure the system's database through a text-based user interface:
$ sudo -u moonfire-nvr moonfire-nvr config 2>debug-log
diff --git a/moonfire.sublime-workspace b/moonfire.sublime-workspace
new file mode 100644
index 0000000..6e9710b
--- /dev/null
+++ b/moonfire.sublime-workspace
@@ -0,0 +1,719 @@
+{
+ "auto_complete":
+ {
+ "selected_items":
+ [
+ [
+ "_recordin",
+ "_recordingsRange"
+ ],
+ [
+ "_record",
+ "_recordingsRange"
+ ],
+ [
+ "selec",
+ "selectedRange"
+ ],
+ [
+ "jquer",
+ "jqueryUIBundleModules"
+ ],
+ [
+ "rec",
+ "recordingClickHandler"
+ ],
+ [
+ "recordin",
+ "recordingsView"
+ ],
+ [
+ "timeF",
+ "timeFormatter"
+ ],
+ [
+ "timeSta",
+ "formatTimeStamp90k"
+ ],
+ [
+ "form",
+ "formatTime90k"
+ ],
+ [
+ "ret",
+ "returns\t@returns"
+ ],
+ [
+ "webpack",
+ "webpackConfig2"
+ ],
+ [
+ "webPackConf",
+ "webpackConfig2"
+ ],
+ [
+ "project",
+ "projectRoot"
+ ],
+ [
+ "prject",
+ "projectRoot"
+ ],
+ [
+ "datepi",
+ "datepicker"
+ ],
+ [
+ "_time",
+ "_timeFormatter"
+ ],
+ [
+ "topi",
+ "toPickerTimeId"
+ ],
+ [
+ "fromP",
+ "fromPickerTimeId"
+ ],
+ [
+ "toP",
+ "toPickerId"
+ ],
+ [
+ "star",
+ "startTime90k"
+ ],
+ [
+ "record",
+ "recordingsView"
+ ],
+ [
+ "update",
+ "updateRecordings"
+ ],
+ [
+ "new",
+ "newTimeFormat"
+ ],
+ [
+ "formatT",
+ "formatTime90K"
+ ],
+ [
+ "_camer",
+ "_cameraRange"
+ ],
+ [
+ "trimmed",
+ "trimmedAgainst"
+ ],
+ [
+ "dur",
+ "duration90k"
+ ],
+ [
+ "formatTim",
+ "formatTimeStamp90K"
+ ],
+ [
+ "le",
+ "length"
+ ],
+ [
+ "displa",
+ "displayObjects"
+ ],
+ [
+ "checkb",
+ "checkboxSelector"
+ ],
+ [
+ "dom",
+ "domElement"
+ ],
+ [
+ "came",
+ "cameraCheckBoxDOM"
+ ],
+ [
+ "camera",
+ "cameraJson"
+ ],
+ [
+ "to",
+ "totalSampleFileBytes"
+ ],
+ [
+ "vide",
+ "videoSampleEntryHeight"
+ ],
+ [
+ "vieo",
+ "videoSampleEntryWidth"
+ ],
+ [
+ "cam",
+ "cameraJson"
+ ],
+ [
+ "Recordin",
+ "RecordingY"
+ ],
+ [
+ "Js",
+ "jsonwrapper"
+ ],
+ [
+ "end",
+ "endTime90k"
+ ],
+ [
+ "js",
+ "jsonData"
+ ],
+ [
+ "recor",
+ "recordingJson"
+ ],
+ [
+ "start",
+ "startTime90k"
+ ],
+ [
+ "ra",
+ "rate"
+ ],
+ [
+ "root",
+ "rootFinder"
+ ],
+ [
+ "Prod_Med",
+ "Prod_Media_PRF"
+ ],
+ [
+ "Prod_Bill",
+ "Prod_Billing_Portal"
+ ],
+ [
+ "Prod_C",
+ "Prod_CSP_BD"
+ ],
+ [
+ "Prod_M",
+ "Prod_Media"
+ ],
+ [
+ "Prod_DA",
+ "Prod_DASRT"
+ ],
+ [
+ "Plat",
+ "PlatQA"
+ ],
+ [
+ "Pro",
+ "Prod_DDC"
+ ],
+ [
+ "Prod",
+ "Prod_DDC"
+ ],
+ [
+ "Pr",
+ "Prod_DDC"
+ ],
+ [
+ "Collee",
+ "ColleenK"
+ ],
+ [
+ "Coll",
+ "ColleenK"
+ ],
+ [
+ "ISab",
+ "ISabine"
+ ],
+ [
+ "ro",
+ "roam_conf"
+ ],
+ [
+ "_debugCe",
+ "_debugCellValue"
+ ],
+ [
+ "debug",
+ "debugCellValue"
+ ],
+ [
+ "zami",
+ "ZamiLoadSensors"
+ ],
+ [
+ "execSes",
+ "execSensorsCalibrateZero"
+ ]
+ ]
+ },
+ "buffers":
+ [
+ ],
+ "build_system": "",
+ "build_system_choices":
+ [
+ ],
+ "build_varint": "",
+ "command_palette":
+ {
+ "height": 359.0,
+ "last_filter": "pack ins ",
+ "selected_items":
+ [
+ [
+ "pack ins ",
+ "Package Control: Install Package"
+ ],
+ [
+ "pac list",
+ "Package Control: List Packages"
+ ],
+ [
+ "pac ins",
+ "Package Control: Install Package"
+ ],
+ [
+ "pack rem",
+ "Package Control: Remove Package"
+ ],
+ [
+ "pack list",
+ "Package Control: List Packages"
+ ],
+ [
+ "pack ins",
+ "Package Control: Install Package"
+ ],
+ [
+ "pac inst",
+ "Package Control: Install Package"
+ ],
+ [
+ "pac li",
+ "Package Control: List Packages"
+ ],
+ [
+ "pac re",
+ "Package Control: Remove Package"
+ ],
+ [
+ "pack li",
+ "Package Control: List Packages"
+ ],
+ [
+ "insta",
+ "Package Control: Install Package"
+ ],
+ [
+ "package",
+ "Package Control: Remove Package"
+ ],
+ [
+ " instal",
+ "Package Control: Install Package"
+ ],
+ [
+ "pack",
+ "Preferences: Browse Packages"
+ ],
+ [
+ "inst",
+ "Package Control: Install Package"
+ ],
+ [
+ "rem",
+ "Package Control: Remove Package"
+ ],
+ [
+ "remove",
+ "Package Control: Remove Package"
+ ],
+ [
+ "in",
+ "Package Control: Install Package"
+ ],
+ [
+ "re",
+ "Package Control: Remove Package"
+ ],
+ [
+ "install p",
+ "Package Control: Install Package"
+ ],
+ [
+ "install",
+ "Package Control: Install Package"
+ ],
+ [
+ "insall",
+ "Package Control: Install Package"
+ ],
+ [
+ "instal",
+ "Package Control: Install Package"
+ ],
+ [
+ "packa",
+ "Package Control: Install Package"
+ ]
+ ],
+ "width": 476.0
+ },
+ "console":
+ {
+ "height": 322.0,
+ "history":
+ [
+ "import urllib.request,os,hashlib; h = '6f4c264a24d933ce70df5dedcf1dcaee' + 'ebe013ee18cced0ef93d5f746d80ef60'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)",
+ "import urllib.request,os,hashlib; h = 'eb2297e1a458f27d836c04bb0cbaf282' + 'd0e7a3098092775ccb37ca9d6b2e4b7d'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)"
+ ]
+ },
+ "distraction_free":
+ {
+ "menu_visible": true,
+ "show_minimap": false,
+ "show_open_files": false,
+ "show_tabs": false,
+ "side_bar_visible": false,
+ "status_bar_visible": false
+ },
+ "file_history":
+ [
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/Range.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/Recording.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/support/RecordingFormatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/Range90k.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/RecordingsView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/CameraView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/CalendarTSRange.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/NVRSettingsView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/NVRApplication.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/index.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/MoonfireAPI.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/assets/index.html",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/DatePickerView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/CheckboxGroupView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/views/CalendarView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/support/URLBuilder.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/support/TimeFormatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/support/Time90kParser.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/webpack/parts/Settings.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/webpack/base.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/webpack/dev.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/webpack/NVRSettings.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/webpack/prod.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/settings-nvr-local.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/settings-nvr.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/Camera.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/models/JsonWrapper.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/node_modules/jquery-ui/ui/widgets/datepicker.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-dist/vendor.bundle.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-dist/nvr.bundle.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/assets/index.css",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/HTMLBeautify/HTMLBeautify.sublime-settings",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/moonfire.sublime-project",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/ESLint-Formatter.sublime-settings",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/ESLint-Formatter/ESLint-Formatter.sublime-settings",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/.eslintrc.json",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/.prettierrc.json",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/ui-src/lib/IteratorUtils.js",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/JsPrettier/JsPrettier.sublime-settings",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/JsPrettier.sublime-settings",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/JsFormat/JsFormat.sublime-settings",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/guide/easy-install.md",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/guide/developing-ui.md",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire-nvr/README.md",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/Preferences.sublime-settings",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/Default/Preferences.sublime-settings",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/DatePickerView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/TimeFormatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/models/Camera.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/Time90kParser.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/models/CalendarTSRange.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/index.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/RecordingFormatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/CalendarView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/IteratorUtils.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/models/Range.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/CameraView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/RecordingsView.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/index.html",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/models/Range90k.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/models/Recording.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/recordingformatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/views/RecordingsDisplay.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/webpack/base.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/webpack/dev.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/jsonwrapper.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/recordingsdisplay.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/recording.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/camera.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/range90k.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/lib/cameradisplay.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/recordingsdisplay.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/webpack/prod.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/camera.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/recordingstable.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/recordingformatter.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/jsonwrapper.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/package.json",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/webpack.config.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/ui-src/recording.js",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/.eslintrc.js",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/JsFormat.sublime-settings",
+ "/Users/dolf/Documents/Dev/Dolf/moonfire/webpack/full.config.js",
+ "/Users/dolf/Desktop/git config",
+ "/Users/dolf/Desktop/Signatures/dolf-simple.html",
+ "/Users/dolf/Desktop/Signatures/reg-simple.html",
+ "/Users/dolf/Desktop/dolf.vcf",
+ "/Users/dolf/Desktop/Dolf HTML.html",
+ "/Users/dolf/Desktop/Signatures/sig/dolf-home.vcf",
+ "/Users/dolf/Desktop/Signatures/dolf-home.html",
+ "/Users/dolf/Desktop/Starfield HTML.html",
+ "/Users/dolf/Desktop/Signatures/starfield-medium.html",
+ "/Users/dolf/Desktop/Signatures/reg-medium.html",
+ "/Users/dolf/Desktop/Signatures/dolf-medium.html",
+ "/Users/dolf/Desktop/Signatures/dolf-dolf.html",
+ "/Users/dolf/Desktop/recipients.txt",
+ "/Users/dolf/Desktop/t2.txt",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/HTML-CSS-JS Prettify/.jsbeautifyrc.defaults.json",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/.jsbeautifyrc",
+ "/Users/dolf/Desktop/x.js",
+ "/Users/dolf/Documents/Dolf-Sites/starreveld.com/index.html",
+ "/Users/dolf/Desktop/types.txt",
+ "/Users/dolf/Desktop/t.txt",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/User/JavascriptBeautify.sublime-settings",
+ "/Users/dolf/Library/Application Support/Sublime Text 3/Packages/Javascript Beautify/JavascriptBeautify.sublime-settings",
+ "/Users/dolf/Documents/Dolf-Sites/starreveld.com/Scripts/mmenudom.js",
+ "/Users/dolf/Documents/Dolf-Sites/starreveld.com/crossdomain.xml",
+ "/Users/dolf/Downloads/weather.dms",
+ "/Users/dolf/hta.txt",
+ "/Users/dolf/Desktop/x/Roots/FunctionNext.java",
+ "/Users/dolf/Desktop/x/Roots/Interval.java",
+ "/Users/dolf/Desktop/x/Roots/Function1.java",
+ "/Users/dolf/Desktop/x/RootsMain.java",
+ "/Users/dolf/Documents/Dev/Dolf/ubnt-logview/README",
+ "/Users/dolf/Desktop/goldens.dot",
+ "/Users/dolf/Documents/Dev/Anderson/READ ME",
+ "/Users/dolf/Desktop/x/Animals/Dog.java",
+ "/Users/dolf/Desktop/x/Animals/IAnimal.java",
+ "/Users/dolf/Desktop/x/Animals/Animal.java",
+ "/Users/dolf/Desktop/x/Animals/Cat.java",
+ "/Users/dolf/Desktop/x/AniMain.java",
+ "/Users/dolf/Desktop/x/Animals/Crate.java",
+ "/Users/dolf/Dropbox/Dolf/Dropbox/Akamai/Drawings/akamai.dot",
+ "/Users/dolf/Desktop/x/Animals/BaseAnimal.java",
+ "/Users/dolf/Desktop/T2.java",
+ "/Users/dolf/Desktop/T1.java",
+ "/Users/dolf/Desktop/main.java",
+ "/Users/dolf/Downloads/MTR"
+ ],
+ "find":
+ {
+ "height": 39.0
+ },
+ "find_in_files":
+ {
+ "height": 101.0,
+ "where_history":
+ [
+ ""
+ ]
+ },
+ "find_state":
+ {
+ "case_sensitive": false,
+ "find_history":
+ [
+ "Recording ra",
+ "Trim ",
+ "\\'Trim",
+ "'Trim ",
+ "trim range",
+ "Range90k",
+ "Camera range",
+ "_updateRecordings",
+ "updateRecord",
+ "recordingsRange",
+ "fetch",
+ "_updateRecordings",
+ "onTrimChange",
+ "onSelec",
+ "trimHandler",
+ "addOnedateOnlyThenEndOfDay",
+ "parseDateTime90k",
+ "\\[description\\]",
+ "[description]",
+ "console.log",
+ "new range",
+ "recordingsUrl\\(",
+ "recordingsUrl(",
+ "_videoLength",
+ "_videoLengthHandler",
+ "onVideoLengthChange",
+ "fetch(",
+ "onTimeCh",
+ "videoLength",
+ "fetch\\(",
+ "fetch(",
+ "#split",
+ "fetch(",
+ "onVideoLengthChange",
+ "_videoLengthHandler",
+ "videoLengthE",
+ "_trimHandler",
+ "onTrimChange",
+ "initializeWith",
+ "setupCalendar",
+ "onTrimChange",
+ "dsiabl",
+ "_pickerTimeChanged",
+ "_updateRangeDates",
+ "_setRangeTime",
+ "new time",
+ "_informRangeChange",
+ "_setRangetim",
+ "timeFmt",
+ "('.*'),",
+ "dialog",
+ "import",
+ "require",
+ "dialog",
+ "jquery-ui/themes/base/button.css",
+ "('.*'),",
+ "jquery-ui/themes/base/button.css",
+ "<<",
+ "process.env.NODE_ENV",
+ "'NVR",
+ "NVR",
+ "recording cli"
+ ],
+ "highlight": true,
+ "in_selection": false,
+ "preserve_case": false,
+ "regex": true,
+ "replace_history":
+ [
+ "import(/* webpackChunkName: \"jquery\" */ $1);",
+ "require($1);"
+ ],
+ "reverse": false,
+ "show_context": false,
+ "use_buffer2": false,
+ "whole_word": false,
+ "wrap": true
+ },
+ "groups":
+ [
+ {
+ "sheets":
+ [
+ ]
+ }
+ ],
+ "incremental_find":
+ {
+ "height": 23.0
+ },
+ "input":
+ {
+ "height": 35.0
+ },
+ "layout":
+ {
+ "cells":
+ [
+ [
+ 0,
+ 0,
+ 1,
+ 1
+ ]
+ ],
+ "cols":
+ [
+ 0.0,
+ 1.0
+ ],
+ "rows":
+ [
+ 0.0,
+ 1.0
+ ]
+ },
+ "menu_visible": true,
+ "output.SublimeLinter":
+ {
+ "height": 0.0
+ },
+ "output.SublimeLinter Messages":
+ {
+ "height": 102.0
+ },
+ "output.find_results":
+ {
+ "height": 300.0
+ },
+ "output.unsaved_changes":
+ {
+ "height": 102.0
+ },
+ "pinned_build_system": "",
+ "project": "moonfire.sublime-project",
+ "replace":
+ {
+ "height": 70.0
+ },
+ "save_all_on_build": true,
+ "select_file":
+ {
+ "height": 0.0,
+ "last_filter": "",
+ "selected_items":
+ [
+ ],
+ "width": 0.0
+ },
+ "select_project":
+ {
+ "height": 0.0,
+ "last_filter": "",
+ "selected_items":
+ [
+ ],
+ "width": 0.0
+ },
+ "select_symbol":
+ {
+ "height": 0.0,
+ "last_filter": "",
+ "selected_items":
+ [
+ ],
+ "width": 0.0
+ },
+ "selected_group": 0,
+ "settings":
+ {
+ },
+ "show_minimap": true,
+ "show_open_files": false,
+ "show_tabs": true,
+ "side_bar_visible": true,
+ "side_bar_width": 205.0,
+ "status_bar_visible": true,
+ "template_settings":
+ {
+ }
+}
diff --git a/package.json b/package.json
index d984618..1552e2b 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,8 @@
"url": "https://github.com/scottlamb/moonfire-nvr/issues"
},
"scripts": {
- "build": "webpack && ln -f ui-src/index.html ui-dist/"
+ "start": "webpack-dev-server --mode development --config webpack/dev.config.js --progress",
+ "build": "webpack --mode production --config webpack/prod.config.js"
},
"dependencies": {
"jquery": "^3.2.1",
@@ -18,12 +19,17 @@
"version": "0.1.0",
"devDependencies": {
"babel-core": "^6.26.0",
- "babel-loader": "^7.1.2",
- "babel-minify-webpack-plugin": "^0.2.0",
+ "babel-loader": "^7.1.4",
+ "babel-minify-webpack-plugin": "^0.3.0",
"babel-preset-env": "^1.6.1",
- "css-loader": "^0.28.7",
- "file-loader": "^1.1.5",
+ "clean-webpack-plugin": "^0.1.18",
+ "css-loader": "^0.28.10",
+ "file-loader": "^1.1.11",
+ "html-webpack-plugin": "^3.0.6",
"style-loader": "^0.19.0",
- "webpack": "^3.8.1"
+ "webpack": "^4.0.1",
+ "webpack-cli": "^2.0.10",
+ "webpack-dev-server": "^3.1.0",
+ "webpack-merge": "^4.1.2"
}
}
diff --git a/prep.sh b/prep.sh
deleted file mode 100755
index bcbd410..0000000
--- a/prep.sh
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/bin/bash -x
-#
-# This file is part of Moonfire NVR, a security camera network video recorder.
-# Copyright (C) 2016 Scott Lamb
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link the code of portions of this program with the
-# OpenSSL library under certain conditions as described in each
-# individual source file, and distribute linked combinations including
-# the two.
-#
-# You must obey the GNU General Public License in all respects for all
-# of the code used other than OpenSSL. If you modify file(s) with this
-# exception, you may extend this exception to your version of the
-# file(s), but you are not obligated to do so. If you do not wish to do
-# so, delete this exception statement from your version. If you delete
-# this exception statement from all source files in the program, then
-# also delete it here.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#
-# Script to prepare for moonfire-nvr operations
-#
-# Command line options:
-# -S: Skip apt-get update and install
-#
-
-# Configuration variables. Should only need minimal, or zero, changes.
-# Empty values will use defaults. For convenience, we attempt to read
-# customizations from prep.config first
-#
-if [ -r prep.config ]; then
- . prep.config
-fi
-
-# User and group
-# Default: or whatever is in $NVR_USER (default "moonfire-nvr")
-#
-#NVR_USER=
-#NVR_GROUP=
-
-# Port for web server
-# Default: 8080
-#
-#NVR_PORT=
-
-# This should, ideally, be a location on flash storage under which the
-# moonfire user's home directory will be created.
-# Default: "/var/lib"
-#
-#NVR_HOME_BASE=
-
-# Set to mountpoint of media directory, empty to stay in home directory
-# Default: empty
-#SAMPLES_DIR=/media/nvr/samples
-
-# Set to path for media directory relative to mountpoint
-# Default: "samples"
-#
-#SAMPLES_DIR_NAME=
-
-# Binary location
-# Default: "/usr/local/bin/moonfire-nvr"
-#
-#SERVICE_BIN=
-
-# Resource files location
-# Default: "/usr/local/lib/moonfire-nvr"
-#LIB_DIR=/usr/local/lib/moonfire-nvr
-
-# Service name
-# Default: "moonfire-nvr"
-#
-#SERVICE_NAME=
-
-# Service Description
-# Default: "Moonfire NVR"
-#
-#SERVICE_DESC=
-
-# --------------------------------------------------------------------
-# Derived variables: Do not modify!
-#
-# Determine directory path of this script
-#
-SOURCE="${BASH_SOURCE[0]}"
-while [ -h "$SOURCE" ]; do # resolve $SOURCE until file no longer a symlink
- DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
- SOURCE="$(readlink "$SOURCE")"
- # if $SOURCE was relative symlink, resolve relative to path of symlink
- [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
-done
-
-SRC_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"/src
-NVR_USER="${NVR_USER:-moonfire-nvr}"
-NVR_GROUP="${NVR_GROUP:-$NVR_USER}"
-NVR_PORT="${NVR_PORT:-8080}"
-NVR_HOME_BASE="${NVR_HOME_BASE:-/var/lib}"
-NVR_HOME="${NVR_HOME_BASE}/${NVR_USER}"
-DB_DIR="${DB_DIR:-$NVR_HOME/db}"
-SAMPLES_DIR_NAME="${SAMPLES_DIR_NAME:-samples}"
-SAMPLES_DIR="${SAMPLES_DIR:-$NVR_HOME/$SAMPLES_DIR_NAME}"
-SERVICE_NAME="${SERVICE_NAME:-moonfire-nvr}"
-SERVICE_DESC="${SERVICE_DESC:-Moonfire NVR}"
-SERVICE_BIN="${SERVICE_BIN:-/usr/local/bin/moonfire-nvr}"
-LIB_DIR="${UI_DIR:-/usr/local/lib/moonfire-nvr}"
-
-# Process command line options
-#
-while getopts ":DS" opt; do
- case $opt in
- S) SKIP_APT=1
- ;;
- :)
- echo "Option -$OPTARG requires an argument." >&2
- exit 1
- ;;
- \?)
- echo "Invalid option: -$OPTARG" >&2
- exit 1
- ;;
- esac
-done
-
-# Setup all apt packages we need
-#
-echo 'Preparing and downloading packages we need...'; echo
-if [ "${SKIP_APT:-0}" != 1 ]; then
- sudo apt-get update
- sudo apt-get install \
- build-essential \
- libavcodec-dev \
- libavformat-dev \
- libavutil-dev \
- libncurses5-dev \
- libncursesw5-dev \
- libsqlite3-dev
-fi
-
-# Check if binary is installed. Setup for build if it is not
-#
-if [ ! -x "${SERVICE_BIN}" ]; then
- echo "Binary not installed, building..."; echo
- if ! cargo --version; then
- echo "cargo not installed/working."
- echo "Install Rust (see http://rustup.us) first."
- echo
- exit 1
- fi
- if ! cargo test; then
- echo "test failed. Try to run the following manually for more info"
- echo "RUST_TEST_THREADS=1 cargo test --verbose"
- echo
- exit 1
- fi
- if ! cargo build --release; then
- echo "Server build failed."
- echo "RUST_TEST_THREADS=1 cargo build --release --verbose"
- echo
- exit 1
- fi
- sudo install -m 755 target/release/moonfire-nvr ${SERVICE_BIN}
- if [ -x "${SERVICE_BIN}" ]; then
- echo "Binary installed..."; echo
- else
- echo "Build failed to install..."; echo
- exit 1
- fi
-fi
-if [ ! -d "${LIB_DIR}/ui" ]; then
- echo "UI directory doesn't exist, building..."; echo
- if ! yarn --version; then
- echo "yarn not installed/working."
- echo "Install from https://yarnpkg.com/ first."
- echo
- exit 1
- fi
- if ! yarn; then
- echo "UI package installation failed."
- exit 1
- fi
- if ! yarn build; then
- echo "UI build failed."
- exit 1
- fi
- sudo mkdir "${LIB_DIR}"
- sudo cp -R ui-dist "${LIB_DIR}/ui"
-fi
-
-# Create user and groups
-#
-echo 'Create user/group and directories we need...'; echo
-sudo addgroup --quiet --system ${NVR_GROUP}
-sudo adduser --quiet --system ${NVR_USER} --ingroup "${NVR_GROUP}" --home "${NVR_HOME}"
-if [ ! -d "${NVR_HOME}" ]; then
- sudo mkdir "${NVR_HOME}"
-fi
-sudo chown ${NVR_USER}:${NVR_GROUP} "${NVR_HOME}"
-
-# Prepare samples directory
-#
-if [ -z "${SAMPLES_DIR}" ]; then
- SAMPLES_PATH="${NVR_HOME}/${SAMPLES_DIR_NAME}"
- if [ ! -d "${SAMPLES_PATH}" ]; then
- sudo -u ${NVR_USER} -H mkdir "${SAMPLES_PATH}"
- else
- chown -R ${NVR_USER}.${NVR_USER} "${SAMPLES_PATH}"
- fi
-else
- SAMPLES_PATH="${SAMPLES_DIR}"
- if [ ! -d "${SAMPLES_PATH}" ]; then
- sudo mkdir "${SAMPLES_PATH}"
- echo ">>> Do not forget to edit /etc/fstab to mount samples media"; echo
- fi
- chown -R ${NVR_USER}.${NVR_USER} "${SAMPLES_PATH}"
-fi
-
-
-# Prepare for sqlite directory and set schema into db
-#
-echo 'Create database...'; echo
-if [ ! -d "${DB_DIR}" ]; then
- sudo -u ${NVR_USER} -H mkdir "${DB_DIR}"
-fi
-sudo -u ${NVR_USER} -H ${SERVICE_BIN} init --db-dir="${DB_DIR}"
-
-# Prepare service files
-#
-SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
-sudo sh -c "cat > ${SERVICE_PATH}" <
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the
+# OpenSSL library under certain conditions as described in each
+# individual source file, and distribute linked combinations including
+# the two.
+#
+# You must obey the GNU General Public License in all respects for all
+# of the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do
+# so, delete this exception statement from your version. If you delete
+# this exception statement from all source files in the program, then
+# also delete it here.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
+
+initEnvironmentVars
+
+# Process command line options
+#
+while getopts ":Bt" opt; do
+ case $opt in
+ B) BUILD_ONLY=1
+ ;;
+ t) IGNORE_TESTS=1
+ ;;
+ :)
+ echo_fatal -x "Option -$OPTARG requires an argument."
+ ;;
+ \?)
+ echo_fatal "Invalid option: -$OPTARG"
+ ;;
+ esac
+done
+
+# Setup cargo if files are present
+#
+initCargo
+
+# Check environment
+#
+
+rv=$(getVersion rustc 0.0)
+if ! versionAtLeast "$rv" "$RUSTC_MIN_VERSION"; then
+ echo_fatal -x "rustc not present or version less than $RUSTC_MIN_VERSION"
+fi
+
+cv=$(getVersion cargo 0.0)
+if ! versionAtLeast "$cv" "$CARGO_MIN_VERSION"; then
+ echo_fatal -x "cargo not present or version less than $CARGO_MIN_VERSION"
+fi
+
+yv=$(getVersion yarn 0.0)
+if ! versionAtLeast "$yv" "$YARN_MIN_VERSION"; then
+ echo_fatal -x "yarn not present or version less than $YARN_MIN_VERSION"
+fi
+
+# Building main server component
+#
+if [ "${FORCE_CLEAN:-0}" -eq 1 ]; then
+ echo_info -x "Forcing clean server build..."
+ cargo clean
+fi
+
+echo_info -x "Building test version..."
+if ! cargo test; then
+ echo_error -x "test failed." "Try to run the following manually for more info" \
+ "RUST_TEST_THREADS=1 cargo test --verbose" ''
+ if [ "${IGNORE_TESTS:-0}" -ne 1 ]; then
+ exit 1
+ fi
+fi
+if ! cargo build --release; then
+ echo_error -x "Server/release build failed." "Try to run the following manually for more info" \
+ "RUST_TEST_THREADS=1 cargo build --release --verbose" ''
+ exit 1
+fi
+
+# Building UI components
+#
+echo_info -x "Building UI components..."
+if ! yarn build; then
+ echo_fatal -x "UI build failed." "yarn build"
+fi
+
+# Stop if build only is desired
+#
+if [ "${BUILD_ONLY:-0}" != 0 ]; then
+ echo_info -x "Build (only) complete, exiting"
+ exit 0
+fi
+. `dirname ${BASH_SOURCE[0]}`/install.sh
diff --git a/scripts/clean.sh b/scripts/clean.sh
new file mode 100755
index 0000000..4cee633
--- /dev/null
+++ b/scripts/clean.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+#
+rm -fr node_modules
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100755
index 0000000..995e71d
--- /dev/null
+++ b/scripts/install.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+#
+# This file is part of Moonfire NVR, a security camera network video recorder.
+# Copyright (C) 2016-17 Scott Lamb
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the
+# OpenSSL library under certain conditions as described in each
+# individual source file, and distribute linked combinations including
+# the two.
+#
+# You must obey the GNU General Public License in all respects for all
+# of the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do
+# so, delete this exception statement from your version. If you delete
+# this exception statement from all source files in the program, then
+# also delete it here.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
+
+# Determine directory path of this script
+#
+initEnvironmentVars
+
+# Process command line options
+#
+while getopts ":s" opt; do
+ case $opt in
+ s) NO_SERVICE=1
+ ;;
+ :)
+ echo_fatal "Option -$OPTARG requires an argument."
+ ;;
+ \?)
+ echo_fatal "Invalid option: -$OPTARG"
+ ;;
+ esac
+done
+
+sudo_warn
+
+sudo install -m 755 target/release/moonfire-nvr ${SERVICE_BIN}
+if [ -x "${SERVICE_BIN}" ]; then
+ echo_info -x "Server Binary installed..."
+else
+ echo_info -x "Server build failed to install..."
+ exit 1
+fi
+if [ ! -d "${LIB_DIR}" ]; then
+ sudo mkdir "${LIB_DIR}"
+fi
+if [ -d "ui-dist" ]; then
+ sudo mkdir -p "${LIB_DIR}/ui"
+ sudo cp -R ui-dist/. "${LIB_DIR}/ui/"
+ sudo chown -R ${NVR_USER}:${NVR_GROUP} "${LIB_DIR}/ui/"
+ echo_info -x "Server UI installed..."
+else
+ echo_fatal -x "Server UI failed to build or install..."
+fi
+
+if [ "${NO_SERVICE:-0}" != 0 ]; then
+ echo_info -x "Not configuring systemctl service. Done"
+ exit 0
+fi
+
+# Make sure user was created
+#
+if ! userExists "${NVR_USER}"; then
+ echo_fatal -x "NVR_USER=${NVR_USER} was not created. Do so manually or run the setup script."
+fi
+
+pre_install_prep
+
+
+# Prepare service files for socket when using priviliged port
+#
+SOCKET_SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.socket"
+if [ $NVR_PORT -lt 1024 ]; then
+ echo_fatal -x "NVR_PORT ($NVR_PORT) < 1024: priviliged ports not supported"
+fi
+
+# Prepare service files for moonfire
+#
+SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
+sudo sh -c "cat > ${SERVICE_PATH}" <
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the
+# OpenSSL library under certain conditions as described in each
+# individual source file, and distribute linked combinations including
+# the two.
+#
+# You must obey the GNU General Public License in all respects for all
+# of the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do
+# so, delete this exception statement from your version. If you delete
+# this exception statement from all source files in the program, then
+# also delete it here.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+if [ -z "$BASH_VERSION" ]; then
+ echo "Script must run using bash (/bin/bash), not dash (/bin/sh)."
+ exit 1
+fi
+
+# Useful constants
+#
+NODE_MIN_VERSION="8"
+YARN_MIN_VERSION="1.0"
+CARGO_MIN_VERSION="0.2"
+RUSTC_MIN_VERSION="1.17"
+FFMPEG_MIN_VERSION="55.1.101"
+FFMPEG_RELEASE_VERSION="3.4"
+
+normalizeDirPath()
+{
+ echo "$( cd -P "$1" && pwd )"
+}
+
+resolvePath()
+{
+ local d="$1"
+ while [ -h "$d" ]; do # resolve $d until file no longer a symlink
+ DIR="$( cd -P "$( dirname "$d" )" && pwd )"
+ d="$(readlink "$d")"
+ # if $d was rel symlink, resolve relative to path of symlink
+ [[ "$d" != /* ]] && ="$DIR/$d"
+ done
+ echo "$d"
+}
+
+functionsInit()
+{
+ local p="$(resolvePath "${BASH_SOURCE[0]}")"
+ MOONFIRE_DIR="$(normalizeDirPath "`dirname "${p}"`/..")"
+}
+
+read_lines()
+{
+ LINES_READ=()
+ while read -r line; do
+ LINES_READ+=("$line")
+ done
+}
+
+catPrefix()
+{
+ sed -e "s/^/$2/" < "$1"
+}
+
+mkdir_moonfire()
+{
+ sudo -u ${NVR_USER} -H mkdir "$@"
+}
+
+echo_multi()
+{
+ local prefix=''
+ local plus=''
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ # Define a prefix for each line
+ -p) shift; prefix="$1"; shift ;;
+ # Provide extra empty line at end
+ -x) shift; plus=1 ;;
+ # Insert contents of LINES_READ here
+ # Only works as leading option
+ -L) shift; set -- "${LINES_READ[@]}" "$@" ;;
+ # Stop processing options
+ -) shift; break ;;
+ # Non option break out
+ *) break ;;
+ esac
+ done
+
+ local A=("$@")
+ for l in "${A[@]/#/$prefix}"; do
+ echo "$l"
+ done
+ [ -n "$plus" ] && echo
+}
+
+echo_stderr()
+{
+ echo_multi "$@" 1>&2
+}
+
+echo_info()
+{
+ echo_multi -x -p '>>> ' "$@"
+}
+
+
+echo_warn()
+{
+ echo_multi -p 'WARNING: ' "$@" 1>&2
+}
+
+echo_error()
+{
+ echo_multi -p 'ERROR: ' "$@" 1>&2
+}
+
+echo_fatal()
+{
+ echo_error "$@"
+ exit 1;
+}
+
+
+# Read possible user config and then compute all derived environment
+# variables used by the script(s).
+#
+initEnvironmentVars()
+{
+ test -z "${MOONFIRE_DIR}" && functionsInit
+ if [ -r "${MOONFIRE_DIR}/prep.config" ]; then
+ . "${MOONFIRE_DIR}/prep.config"
+ fi
+ SRC_DIR="$(resolvePath "$MOONFIRE_DIR/src")"
+ NVR_USER="${NVR_USER:-moonfire-nvr}"
+ NVR_GROUP="${NVR_GROUP:-$NVR_USER}"
+ NVR_PORT="${NVR_PORT:-8080}"
+ NVR_HOME_BASE="${NVR_HOME_BASE:-/var/lib}"
+ NVR_HOME="${NVR_HOME_BASE}/${NVR_USER}"
+ DB_NAME="${DB_NAME:-db}"
+ DB_DIR="${DB_DIR:-$NVR_HOME/${DB_NAME}}"
+ SAMPLE_FILE_DIR="${SAMPLE_FILE_DIR:-sample}"
+ SAMPLE_MEDIA_DIR="${SAMPLE_MEDIA_DIR:-$NVR_HOME}"
+ SERVICE_NAME="${SERVICE_NAME:-moonfire-nvr}"
+ SERVICE_DESC="${SERVICE_DESC:-Moonfire NVR}"
+ SERVICE_BIN="${SERVICE_BIN:-/usr/local/bin/moonfire-nvr}"
+ LIB_DIR="${UI_DIR:-/usr/local/lib/moonfire-nvr}"
+}
+
+# Create file with confguration variables that are user changeable.
+# Not all variables are included here, only the likely ones to be
+# modified.
+# If the file already exists, it will not be modified
+#
+makePrepConfig()
+{
+ test -z "${MOONFIRE_DIR}" && functionsInit
+ if [ ! -f "${MOONFIRE_DIR}/prep.config" ]; then
+ cat >"${MOONFIRE_DIR}/prep.config" <<-EOF_CONFIG
+ NVR_USER=$NVR_USER
+ NVR_PORT=$NVR_PORT
+ SAMPLE_FILE_DIR=$SAMPLE_FILE_DIR
+ #SAMPLE_MEDIA_DIR=/mount/media
+ SERVICE_NAME=$SERVICE_NAME
+ SERVICE_DESC="$SERVICE_DESC"
+EOF_CONFIG
+ echo_info -x "File prep.config newly created. Inspect and change as necessary." \
+ "When done, re-run this setup script. Currently it contains:"
+ catPrefix "${MOONFIRE_DIR}/prep.config" " "
+ echo_info -x
+ exit 0
+ else
+ echo_info -x "Setting up with variables:"
+ catPrefix "${MOONFIRE_DIR}/prep.config" " "
+ echo_info -x
+ fi
+}
+
+# Extract version data from stdin, possibly grepping first against
+# single argument.
+#
+extractVersion()
+{
+ local pattern="$1"
+
+ if [ -n "${pattern}" ]; then
+ grep "$pattern" | sed -e 's/[^0-9.]*\([0-9. ]*\).*/\1/' | tr -d ' '
+ else
+ sed -e 's/[^0-9.]*\([0-9. ]*\).*/\1/' | tr -d ' '
+ fi
+}
+
+getAVersion()
+{
+ local v=`$1 $2 2>/dev/null | extractVersion`
+ if [ "X${v}" = "X" ]; then echo "$3"; else echo "${v}"; fi
+}
+
+getVersion()
+{
+ getAVersion $1 --version $2
+}
+
+getClassicVersion()
+{
+ getAVersion $1 -version $2
+}
+
+versionAtLeast()
+{
+ v1=(${1//./ } 0 0 0); v1=("${v1[@]:0:3}")
+ v2=(${2//./ } 0 0 0); v2=("${v2[@]:0:3}")
+
+ for i in 0 1 2; do
+ if [ "${v1[$i]}" -gt "${v2[$i]}" ]; then return 0; fi
+ if [ "${v1[$i]}" -lt "${v2[$i]}" ]; then return 1; fi
+ done
+ return 0
+}
+
+initCargo()
+{
+ if [ -r ~/.cargo/env ]; then
+ source ~/.cargo/env
+ fi
+}
+
+userExists()
+{
+ return $(id -u "$1" >/dev/null 2>&1)
+}
+
+groupExists()
+{
+ return $(id -g "$1" >/dev/null 2>&1)
+}
+
+moonfire()
+{
+ case "$1" in
+ start)
+ sudo systemctl start "$2"
+ ;;
+ stop)
+ sudo systemctl stop "$2"
+ ;;
+ esac
+}
+
+sudo_warn()
+{
+ echo_warn -x -p '!!!!! ' \
+ '------------------------------------------------------------------------------' \
+ 'During this script you may be asked to input your root password' \
+ 'This is for the purpose of using the sudo command and is necessary to complete' \
+ 'the script successfully.' \
+ '------------------------------------------------------------------------------'
+}
+
+
+# Prepare for sqlite directory and set schema into db
+#
+setup_db()
+{
+ DB_NAME=${1:-db}
+ if [ ! -d "${DB_DIR}" ]; then
+ echo_info -x 'Create database directory...'
+ mkdir_moonfire -p "${DB_DIR}"
+ fi
+ DB_PATH="${DB_DIR}/${DB_NAME}"
+ if [ ! -f "${DB_PATH}" ]; then
+ echo_info -x 'Create database and initialize...'
+ sudo -u "${NVR_USER}" -H sqlite3 "${DB_PATH}" < "${SRC_DIR}/schema.sql"
+ fi
+}
+
+# Make sure all sample directories and files owned by moonfire
+#
+fix_ownership()
+{
+ sudo chown -R ${NVR_USER}.${NVR_USER} "$1"
+ echo_info -x "Fix ownership of files in \"$1\"..."
+}
+
+# Make sure samples directory is ready
+#
+prep_sample_file_dir()
+{
+ if [ -z "${SAMPLE_MEDIA_DIR}" ]; then
+ echo_fatal -x "SAMPLE_MEDIA_DIR variable not configured. Check configuration."
+ exit 1
+ fi
+ SAMPLE_FILE_PATH="${SAMPLE_MEDIA_DIR}/${SAMPLE_FILE_DIR}"
+ if [ "${SAMPLE_FILE_PATH##${NVR_HOME}}" != "${SAMPLE_FILE_PATH}" ]; then
+ # Under the home directory, create if not there
+ if [ ! -d "${SAMPLE_FILE_PATH}" ]; then
+ echo_info -x "Created sample file directory: \"$SAMPLE_FILE_PATH\"..."
+ mkdir_moonfire -p "${SAMPLE_FILE_PATH}"
+ fi
+ else
+ if [ ! -d "${SAMPLE_FILE_PATH}" ]; then
+ read_lines <<-MSG1
+ Samples directory $SAMPLE_FILE_PATH does not exist.
+ If a mounted file system, make sure /etc/fstab is properly configured,
+ and file system is mounted and directory created.
+ MSG1
+ echo_fatal -L
+ fi
+ fi
+ fix_ownership "${SAMPLE_FILE_PATH}"
+}
+
+# Create user and groups if not there
+#
+prep_moonfire_user()
+{
+ echo_info -x "Create user/group and directories we need..."
+ if ! groupExists "${NVR_GROUP}"; then
+ sudo addgroup --quiet --system ${NVR_GROUP}
+ fi
+ if ! userExists "${NVR_USER}"; then
+ sudo adduser --quiet --system ${NVR_USER} \
+ --ingroup "${NVR_GROUP}" --home "${NVR_HOME}"
+ fi
+ if [ ! -d "${NVR_HOME}" ]; then
+ mkdir_moonfire "${NVR_HOME}"
+ fi
+ sudo chown ${NVR_USER}:${NVR_GROUP} "${NVR_HOME}"
+}
+
+# Correct possible timezone issues
+#
+fix_localtime()
+{
+ if [ ! -L /etc/localtime ] && [ -f /etc/timezone ] &&
+ [ -f "/usr/share/zoneinfo/`cat /etc/timezone`" ]; then
+ echo_info -x "Correcting /etc/localtime setup issue..."
+ sudo ln -sf /usr/share/zoneinfo/`cat /etc/timezone` /etc/localtime
+ fi
+}
+
+pre_install_prep()
+{
+ prep_moonfire_user
+ setup_db db
+ prep_sample_file_dir
+ fix_localtime
+}
+
diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh
new file mode 100755
index 0000000..7a6b723
--- /dev/null
+++ b/scripts/setup-ubuntu.sh
@@ -0,0 +1,184 @@
+#!/bin/bash
+#
+# This file is part of Moonfire NVR, a security camera network video recorder.
+# Copyright (C) 2016-2017 Scott Lamb
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the
+# OpenSSL library under certain conditions as described in each
+# individual source file, and distribute linked combinations including
+# the two.
+#
+# You must obey the GNU General Public License in all respects for all
+# of the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do
+# so, delete this exception statement from your version. If you delete
+# this exception statement from all source files in the program, then
+# also delete it here.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
+
+initEnvironmentVars
+makePrepConfig
+
+
+# Process command line options
+#
+while getopts ":f" opt; do
+ case $opt in
+ f) DONT_BUILD_FFMPEG=1
+ ;;
+ :)
+ echo_fatal "Option -$OPTARG requires an argument."
+ ;;
+ \?)
+ echo_fatal -x "Invalid option: -$OPTARG"
+ ;;
+ esac
+done
+
+sudo_warn
+
+# Setup all apt packages we need
+#
+echo_info -x 'Preparing and downloading packages we need...'
+PKGS="build-essential pkg-config sqlite3"
+#PKGS="$PKGS libavcodec-dev libavformat-dev libavutil-dev"
+PKGS="$PKGS libncurses5-dev libncursesw5-dev"
+PKGS="$PKGS libsqlite3-dev libssl-dev"
+
+# Add yarn before NodeSource so it can all go in one update
+#
+yv=$(getVersion yarn "NA")
+if [ ${yv} = "NA" ]; then
+ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg |\
+ sudo apt-key add -
+ echo "deb https://dl.yarnpkg.com/debian/ stable main" |\
+ sudo tee /etc/apt/sources.list.d/yarn.list
+ PKGS="$PKGS yarn"
+fi
+
+# Check for minimum node version
+#
+nv=$(getVersion node 0)
+if ! versionAtLeast "$nv" "$NODE_MIN_VERSION"; then
+ # Nodesource will also make sure we have apt-transport-https
+ # and will run apt-get-update when done
+ #
+ curl -sL https://deb.nodesource.com/setup_${NODE_MIN_VERSION}.x |
+ sudo -E bash -
+ PKGS="$PKGS nodejs"
+ DO_UPDATE=0
+else
+ PKGS="$PKGS apt-transport-https"
+fi
+
+# Run apt-get update if still necessary
+#
+if [ ${DO_UPDATE:-1} ]; then sudo apt-get update -y; fi
+
+# Install necessary pakackes
+#
+sudo apt-get install -y $PKGS
+sudo apt-get autoremove -y
+echo_info -x
+
+# Check for ffmpeg and install by building if necessary
+# This needs to be done before building moonfire so it can
+# find the right versions of the libraries.
+#
+ffv=`ffmpeg -version 2>/dev/null | extractVersion libavutil`
+ffv=${ffv:-0}
+if ! versionAtLeast "$ffv" "$FFMPEG_MIN_VERSION"; then
+ if [ "${DONT_BUILD_FFMPEG:-0}" -ne 0 ]; then
+ echo_warn -x "ffmpeg version (${ffv}) installed is too old for moonfire." \
+ "Suggest you manually install at least version $FFMPEG_MIN_VERSION of libavutil." \
+ "ffmpeg versions 2.x and 3.x all should work."
+ else
+ OLDDIR=`pwd`
+ cd ..
+ if [ -d FFmpeg ]; then
+ echo_info -x "Removing older FFmpeg directory..."
+ rm -fr FFmpeg
+ fi
+ echo_info -x "Fetching FFmpeg source..."
+ git clone --depth 1 -b "release/${FFMPEG_RELEASE_VERSION}" https://github.com/FFmpeg/FFmpeg.git
+ cd FFmpeg
+ pt=`uname -p 2>& /dev/null`
+ if [ -z "${pt##*86*}" ]; then
+ sudo apt-get install -y yasm
+ fi
+ ./configure --enable-shared
+ make
+ sudo make install
+ sudo ldconfig
+ cd "$OLDDIR"
+ OLDDIR=
+ fi
+else
+ echo_info -x "FFmpeg is already usable..."
+fi
+
+# If cargo appears installed, initialize for using it so rustc can be found
+#
+initCargo
+
+# Make sure we have rust and cargo
+rv=$(getVersion rustc 0.0)
+if ! versionAtLeast "$rv" "$RUSTC_MIN_VERSION"; then
+ echo_info -x "Installing latest rust and cargo..."
+ curl https://sh.rustup.rs -sSf | sh -s - -y
+ initCargo
+fi
+
+cv=$(getVersion cargo "NA")
+if [ ${cv} = "NA" ]; then
+ echo_fatal -x "Cargo is not (properly) installed, but rust is." \
+ "Suggest you install the latest rustup, or manually install cargo."
+ "Install using: curl https://sh.rustup.rs -sSf | sh -s -y"
+fi
+
+# Now make sure we have dev environment and tools for the UI portion
+#
+echo_info -x "Installing all dependencies with yarn..."
+yarn install
+echo_info -x
+
+finish()
+{
+ if [ -z "${OLDDIR}" ]; then
+ cd "${OLDDIR}"
+ fi
+}
+trap finish EXIT
+
+# Rest of prep
+#
+pre_install_prep
+
+
+read_lines <<-'INSTRUCTIONS'
+Unless there are errors above, everything you need should have been installed
+and you are now ready to build, install, and then use moonfire.
+
+Build by executing the script: scripts/build.sh
+Install by executing the script: scripts/install.sh (run automatically by build
+step).
+INSTRUCTIONS
+echo_info -x -p ' ' -L
+
diff --git a/settings-nvr.js b/settings-nvr.js
new file mode 100644
index 0000000..84f8323
--- /dev/null
+++ b/settings-nvr.js
@@ -0,0 +1,37 @@
+// vim: set et ts=2 sw=2:
+//
+
+/**
+ * This module must export a map, but can use a function with no arguments
+ * that returns a map, or a function that receives the "env" and "args"
+ * values from webpack.
+ *
+ * @type {Object}
+ */
+module.exports.settings = {
+ // Project related: use ./ in front of project root relative files!
+ app_src_dir: './ui-src',
+ dist_dir: './ui-dist',
+
+ // App related
+ app_title: 'Moonfire NVR',
+
+ // Where is the server to be found
+ moonfire: {
+ server: 'localhost',
+ port: 8080,
+ },
+
+ /*
+ * In settings override file you can add sections like below on this level.
+ * After processing, anything defined in mode.production or mode.development,
+ * as appropriate based on --mode argument to webpack, will be merged
+ * into the top level of this settings module. This allows you to add to, or
+ * override anything listed above.
+ *
+ * webpack_mode: {
+ * production: {},
+ * development: {},
+ },
+ */
+};
diff --git a/src/cmds/run.rs b/src/cmds/run.rs
index 3b5f96f..4ddbf16 100644
--- a/src/cmds/run.rs
+++ b/src/cmds/run.rs
@@ -55,11 +55,15 @@ Options:
--db-dir=DIR Set the directory holding the SQLite3 index database.
This is typically on a flash device.
[default: /var/lib/moonfire-nvr/db]
- --ui-dir=DIR Set the directory with the user interface files (.html, .js, etc).
+ --ui-dir=DIR Set the directory with the user interface files
+ (.html, .js, etc).
[default: /usr/local/lib/moonfire-nvr/ui]
--http-addr=ADDR Set the bind address for the unencrypted HTTP server.
[default: 0.0.0.0:8080]
--read-only Forces read-only mode / disables recording.
+ --allow-origin=ORIGIN If present, adds a Access-Control-Allow-Origin:
+ header to HTTP responses. This may be useful for
+ Javascript development.
"#;
#[derive(Debug, Deserialize)]
@@ -68,6 +72,7 @@ struct Args {
flag_http_addr: String,
flag_ui_dir: String,
flag_read_only: bool,
+ flag_allow_origin: Option,
}
fn setup_shutdown_future(h: &reactor::Handle) -> Box> {
@@ -111,7 +116,8 @@ pub fn run() -> Result<(), Error> {
}
info!("Directories are opened.");
- let s = web::Service::new(db.clone(), Some(&args.flag_ui_dir), resolve_zone())?;
+ let s = web::Service::new(db.clone(), Some(&args.flag_ui_dir), args.flag_allow_origin,
+ resolve_zone())?;
// Start a streamer for each stream.
let shutdown_streamers = Arc::new(AtomicBool::new(false));
diff --git a/src/web.rs b/src/web.rs
index e54cc18..6322e29 100644
--- a/src/web.rs
+++ b/src/web.rs
@@ -40,7 +40,7 @@ use futures::{future, stream};
use futures_cpupool;
use json;
use http_serve;
-use hyper::header;
+use hyper::header::{self, Header};
use hyper::server::{self, Request, Response};
use mime;
use mp4;
@@ -192,6 +192,7 @@ struct ServiceInner {
db: Arc,
dirs_by_stream_id: Arc>>,
ui_files: HashMap,
+ allow_origin: Option,
pool: futures_cpupool::CpuPool,
time_zone_name: String,
}
@@ -416,7 +417,8 @@ impl ServiceInner {
pub struct Service(Arc);
impl Service {
- pub fn new(db: Arc, ui_dir: Option<&str>, zone: String) -> Result {
+ pub fn new(db: Arc, ui_dir: Option<&str>, allow_origin: Option,
+ zone: String) -> Result {
let mut ui_files = HashMap::new();
if let Some(d) = ui_dir {
Service::fill_ui_files(d, &mut ui_files);
@@ -438,10 +440,15 @@ impl Service {
}
Arc::new(d)
};
+ let allow_origin = match allow_origin {
+ None => None,
+ Some(o) => Some(header::AccessControlAllowOrigin::parse_header(&header::Raw::from(o))?),
+ };
Ok(Service(Arc::new(ServiceInner {
db,
dirs_by_stream_id,
ui_files,
+ allow_origin,
pool: futures_cpupool::Builder::new().pool_size(1).name_prefix("static").create(),
time_zone_name: zone,
})))
@@ -466,8 +473,11 @@ impl Service {
};
let (p, mime) = match e.file_name().to_str() {
Some(n) if n == "index.html" => ("/".to_owned(), mime::TEXT_HTML),
- Some(n) if n.ends_with(".js") => (format!("/{}", n), mime::TEXT_JAVASCRIPT),
Some(n) if n.ends_with(".html") => (format!("/{}", n), mime::TEXT_HTML),
+ Some(n) if n.ends_with(".ico") => (format!("/{}", n),
+ "image/vnd.microsoft.icon".parse().unwrap()),
+ Some(n) if n.ends_with(".js") => (format!("/{}", n), mime::TEXT_JAVASCRIPT),
+ Some(n) if n.ends_with(".map") => (format!("/{}", n), mime::TEXT_JAVASCRIPT),
Some(n) if n.ends_with(".png") => (format!("/{}", n), mime::IMAGE_PNG),
Some(n) => {
warn!("UI directory file {:?} has unknown extension; skipping", n);
@@ -509,6 +519,11 @@ impl server::Service for Service {
Path::NotFound => self.0.not_found(),
Path::Static => self.0.static_file(&req),
};
+ let res = if let Some(ref o) = self.0.allow_origin {
+ res.map(|resp| resp.with_header(o.clone()))
+ } else {
+ res
+ };
future::result(res.map_err(|e| {
error!("error: {}", e);
hyper::Error::Incomplete
@@ -570,7 +585,8 @@ mod bench {
let (tx, rx) = ::std::sync::mpsc::channel();
::std::thread::spawn(move || {
let addr = "127.0.0.1:0".parse().unwrap();
- let service = super::Service::new(db.db.clone(), None, "".to_owned()).unwrap();
+ let service = super::Service::new(db.db.clone(), None, None,
+ "".to_owned()).unwrap();
let server = hyper::server::Http::new()
.bind(&addr, move || Ok(service.clone()))
.unwrap();
diff --git a/ui-src/favicon.ico b/ui-src/favicon.ico
new file mode 100644
index 0000000..08e133d
Binary files /dev/null and b/ui-src/favicon.ico differ
diff --git a/ui-src/index.html b/ui-src/index.html
index ce6bfc5..46cebe9 100644
--- a/ui-src/index.html
+++ b/ui-src/index.html
@@ -2,16 +2,14 @@
- moonfire ui
-
+
+ <%= htmlWebpackPlugin.options.title %>