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 %>