├── .gitignore ├── .npmignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── batch-transcode-video.js ├── index-cli.js ├── index.js ├── lib ├── child-promise.js ├── default-options.js ├── help.js ├── progress.js ├── transcode-error.js ├── util.js └── video-file.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .ref 3 | .tmp 4 | .DS_Store 5 | dist 6 | bin 7 | .history -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .ref 2 | .tmp 3 | .DS_Store 4 | .history -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "preLaunchTask": "npm: build", 12 | "stopOnEntry": false, 13 | "outputCapture": "console", 14 | "console": "integratedTerminal", 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/**/*", 17 | "${workspaceFolder}/bin/**/*" 18 | ], 19 | "skipFiles": [ 20 | "${workspaceFolder}/node_modules/**/*.js", 21 | "/**/*.js" 22 | ], 23 | "sourceMaps": true, 24 | "program": "${workspaceFolder}/bin/batch-transcode-video", 25 | "args": [ 26 | "--input", 27 | "~/Videos/test/", 28 | "--output", 29 | "~/Videos/done/", 30 | "--diff" 31 | ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [Unreleased][unreleased] 5 | 6 | ## [v2.0.0] - 2019-10-27 7 | ### Added 8 | - Disable crop detection with `--nocrop` option (Refs #15, Refs #5) 9 | - Skip crop detection entirely (i.e., do not run `detect-crop`) and do not pass a `--crop` value to `transcode-video`. 10 | 11 | ### Changed 12 | - **BREAKING CHANGE:** the `--force` option has been renamed to `--crop` to be consistent with new `--nocrop` option 13 | 14 | ### Fixed 15 | - Be forgiving when checking transcoding result (Refs #17, Refs #14, Refs #10) 16 | - Do not treat it as an error if we cannot get bitrate from finished transcoding job. 17 | - Only look for the text `Encoding done!` in the output to confirm success. 18 | - Do not delete dest files from previous runs (Refs #17, Refs #11) 19 | - The issue here was that a "file already exists" error led to the destination file being deleted (when not using the `--diff` flag), so files from previous runs were marked as errored and then removed. 20 | - Reset font color changes after running `batch-transcode-video --help` 21 | 22 | ## [v1.3.0] - 2019-02-20 23 | ### Added 24 | - Added `--keep` flag to prevent files from being deleted from the output directory. The `--keep` (alias: `-k`) causes `batch-transcode-video` to **never delete any output files**, no matter what happens, **even if the encoding task fails** for the corresponding input file. If you use this option, input files that fail to encode correctly, or finish encoding, will not be deleted from the output folder. Subsequent runs, with or without using the `--diff` option, will not reprocess the failed input files, unless the corresponding output files are manually deleted. 25 | 26 | ## [v1.2.0] - 2017-12-02 27 | ### Added 28 | - Added message about installing the video_transcoding gem if a `TranscodeError` contains `ENOENT` in the error message. 29 | 30 | ### Fixed 31 | - Do not commit generated files to source control (`bin/`, `lib/`). If installing this from the GitHub repo, run `npm run build` to build. 32 | - Upgrade `package.json` dependencies. 33 | - Include original error message in error output for a `TranscodeError`. 34 | 35 | ## [v1.1.0] - 2016-01-19 36 | ### Added 37 | - `--force` now accepts the following argument values: 38 | - a crop value to use for all videos, such as `"0:0:0:0"` 39 | - any other value for when to use the least extreme crop, e.g.: `1` 40 | - Added `--version` and `-v` flags to CLI. 41 | 42 | ### Changed 43 | - Drop child_process spawn for cross-spawn-async. This change should get things working consistently on Windows. Before, batch operations would fail unexpectedly and have to be restarted several times to fully complete. 44 | - Do not install `video_transcoding` gem by default. Do not force install of gem every time module is installed. 45 | - Delete output files that cannot be confirmed. If an output file does not complete and generate a valid log file that can be verified then delete the partial or errored file. 46 | 47 | ## [v1.0.5] - 2015-11-25 48 | ### Fixed 49 | - Summary calculations after `SIGINT` (`ctrl` + `c`) signal were incorrect. 50 | - `BatchTranscodeVideo` reported `isRunning` as `true` after a `SIGINT`. 51 | 52 | ## [v1.0.4] - 2015-11-09 53 | ### Fixed 54 | - Do not show counts of 0 in `fileStatusLine()`. 55 | - Do not show bitrate in summary output unless write was successful. 56 | 57 | ## [v1.0.3] - 2015-11-09 58 | ### Added 59 | - New metrics added to CLI mode summary output: 60 | - Show average bitrate for successful writes. 61 | - Show total and average running time. 62 | - Show speed in MB/s. 63 | - New `--force` flag. If crop detection returns conflicting crop values then just use the least extreme crop value and continue transcoding. 64 | 65 | ## [v1.0.2] - 2015-11-08 66 | ### Fixed 67 | - Fixed `query-handbrake-log` file path bug in Windows. 68 | - Fixed empty summary bar display bug that caused no summary bar to appear when there were no video files found for the arguments given. 69 | 70 | ## [v1.0.1] - 2015-11-08 71 | ### Added 72 | - Take an educated guess at the current percentage and remaining time before there is any data from the child process that can be used to estimate progress. 73 | 74 | ### Changed 75 | - Generate an error when the `detect-crop` returns multiple crop values for a video. 76 | 77 | ### Fixed 78 | - Fixed false positive errors in OS X environment. 79 | 80 | ## [v1.0.0] - 2015-11-08 81 | ### Added 82 | - Added back `--quiet` and `--debug` flags to new ES2015 version of CLI. 83 | - `--debug` mode disables progress indicator and then streams child process to master `stdout`. 84 | - `--quiet` mode now prevents **any** logging to stdout and will only exit `0` on success or `1` on error 85 | 86 | ### Fixed 87 | - Lots of cleanup for Progress class. 88 | - Get `VideoFile` class working with Windows again. 89 | - Fixed broken help flag in CLI mode. 90 | - Move Windows-specific functions to `util.js`. 91 | - Fixed false positive errors in Windows environment. 92 | - Fixed glob error message details. 93 | 94 | ## [v1.0.0-beta] - 2015-11-07 95 | ### Added 96 | - You can now use the module: 97 | - From the CLI as `batch-transcode-video` (default global option). 98 | - Require the ES5 compatible `dist/` files (default local option). 99 | - Require the raw ES2015 files from the root folder `index.js` or `index-cli.js` for the command line version. 100 | - Fancy new CLI: 101 | - Added progress bars to output. 102 | - Added summary that is displayed on exiting the process. 103 | - Add colored summary bar to summary output. 104 | - Estimate remaining time and current percent completion for each file and the entire batch operation even when no data is available from the child process. 105 | 106 | ### Changed 107 | - Entire repo rewritten to use ES2015 108 | - Added `grunt` and `grunt watch` tasks to build ES2015 source 109 | 110 | ### Fixed 111 | - Do not increment time unless running. 112 | - Save stop time when main class errors out. 113 | - Fix percent and time calculations. 114 | 115 | ## [v0.3.1] - 2015-10-27 116 | ### Fixed 117 | - Entire `stdout` buffer was being saved to memory for each child process which was a large amount of data for each transcoding operation. Now only the most recent chunk of lines from a child process `stdout` is saved in memory. 118 | 119 | ## [v0.3.0] - 2015-10-26 120 | ### Added 121 | - Now functions correctly in Windows. Tested in Windows 10 x64. 122 | - Support for `--input` and `--output` options containing Windows paths that have spaces. 123 | ``` 124 | > batch-transcode-video --input my` videos --output other` folder\temp 125 | ``` 126 | - Generate an error for unknown options. 127 | ``` 128 | > batch-transcode-video --hat --debug 129 | ERROR Unrecognized command --hat provided. 130 | If you would like to supply custom options to transcode-video then put 131 | them at end of the command after a double dash "--". For example to pass 132 | the "--dry-run" command to transcode-video: 133 | 134 | batch-transcode-video --input my_videos/ -- --dry-run 135 | ``` 136 | - Added `--help` option to view the manual in the terminal. 137 | 138 | ### Fixed 139 | - Capture child process error events. Previously unhandled errors (e.g. `ENOENT`) when spawning child processes. 140 | - Support absolute source paths for `--input` and `--output` options. 141 | 142 | ## [v0.2.1] - 2015-10-25 143 | ### Fixed 144 | - Unable to `npm install -g batch-transcode-video` due to missing `promise` dependency in `package.json`. 145 | 146 | ## [v0.2.0] - 2015-10-25 147 | ### Fixed 148 | - Unable to `npm install -g batch-transcode-video` due to missing files error. 149 | 150 | ## [v0.1.1] - 2015-10-25 151 | ### Added 152 | - `--diff` flag 153 | Enable this option if you only want to transcode source files that do not exist already in the `output` folder. 154 | - If a destination file already exists in the `output` directory: 155 | - And `diff` is **enabled**, a message will be generated letting you know that the file was skipped (unless `quiet` is enabled). 156 | - And `diff` is **not enabled**, an error will be generated letting you know that the file already exists. 157 | - If you want to transcode a batch of videos in-place (i.e.: without specifying an `output` directory) then you should enabled this option to prevent errors from being generated when the source and destination file names have the same extension. 158 | - For example: trying to transcode a `.mkv` video into a `.mkv` video without supplying an external `output` directory will generate an error unless you specify the `diff` flag. 159 | 160 | ## [v0.1.0] - 2015-10-25 161 | ### Added 162 | - `--flatten` flag 163 | Do not preserve relative directory structure in output directory. If this is enabled, the base output folder will contain all transcoded videos. Note: this option has no effect unless you specify an `output` directory. 164 | - `--quiet` flag 165 | Log only file writes, errors, and finish (e.g.: success, failure) messages. 166 | 167 | [unreleased]: https://github.com/nwronski/batch-transcode-video/compare/v2.0.0...HEAD 168 | [v2.0.0]: https://github.com/nwronski/batch-transcode-video/compare/v1.3.0...v2.0.0 169 | [v1.3.0]: https://github.com/nwronski/batch-transcode-video/compare/v1.2.0...v1.3.0 170 | [v1.2.0]: https://github.com/nwronski/batch-transcode-video/compare/v1.1.0...v1.2.0 171 | [v1.1.0]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.5...v1.1.0 172 | [v1.0.5]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.4...v1.0.5 173 | [v1.0.4]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.3...v1.0.4 174 | [v1.0.3]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.2...v1.0.3 175 | [v1.0.2]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.1...v1.0.2 176 | [v1.0.1]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.0...v1.0.1 177 | [v1.0.0]: https://github.com/nwronski/batch-transcode-video/compare/v1.0.0-beta...v1.0.0 178 | [v1.0.0-beta]: https://github.com/nwronski/batch-transcode-video/compare/v0.3.1...v1.0.0-beta 179 | [v0.3.1]: https://github.com/nwronski/batch-transcode-video/compare/v0.3.0...v0.3.1 180 | [v0.3.0]: https://github.com/nwronski/batch-transcode-video/compare/v0.2.1...v0.3.0 181 | [v0.2.1]: https://github.com/nwronski/batch-transcode-video/compare/v0.2.0...v0.2.1 182 | [v0.2.0]: https://github.com/nwronski/batch-transcode-video/releases/tag/v0.2.0 183 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | require("load-grunt-tasks")(grunt); 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | concat: { 7 | options: { 8 | stripBanners: true, 9 | banner: "#!/usr/bin/env node\n\nprocess.title = 'batch-transcode-video';\n\n", 10 | }, 11 | bin: { 12 | src: ['.tmp/batch-transcode-video.js'], 13 | dest: 'bin/batch-transcode-video', 14 | }, 15 | }, 16 | babel: { 17 | options: { 18 | sourceMap: true, 19 | presets: ['env'] 20 | }, 21 | dist: { 22 | files: [{ 23 | expand: true, 24 | src: ['index.js', 'index-cli.js', 'lib/**/*.js'], 25 | dest: 'dist/' 26 | }] 27 | }, 28 | bin: { 29 | files: { 30 | '.tmp/batch-transcode-video.js': 'batch-transcode-video.js' 31 | } 32 | } 33 | }, 34 | watch: { 35 | dist: { 36 | files: ['index.js', 'index-cli.js', 'lib/**/*.js'], 37 | tasks: ['babel:dist'], 38 | options: { 39 | spawn: false 40 | } 41 | }, 42 | standalone: { 43 | files: ['batch-transcode-video.js'], 44 | tasks: ['babel:bin', 'concat:bin', 'replace:bin'], 45 | options: { 46 | spawn: false 47 | } 48 | }, 49 | config: { 50 | files: ['Gruntfile.js'], 51 | tasks: [], 52 | options: { 53 | reload: true 54 | } 55 | } 56 | }, 57 | replace: { 58 | options: { 59 | patterns: [ 60 | { 61 | match: 'VERSION', 62 | replacement: '<%= pkg.version %>' 63 | } 64 | ] 65 | }, 66 | bin: { 67 | files: [{ 68 | expand: true, 69 | cwd: 'bin/', 70 | src: 'batch-transcode-video', 71 | dest: 'bin/' 72 | }] 73 | } 74 | } 75 | }); 76 | 77 | grunt.registerTask('default', ['babel', 'concat', 'replace']); 78 | }; 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015 Nick Wronski. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # batch-transcode-video 2 | 3 | [![NPM Version Image](https://img.shields.io/npm/v/batch-transcode-video.svg)](https://www.npmjs.com/package/batch-transcode-video) 4 | [![dependencies Status Image](https://img.shields.io/david/nwronski/batch-transcode-video.svg)](https://github.com/nwronski/batch-transcode-video/) 5 | [![devDependencies Status Image](https://img.shields.io/david/dev/nwronski/batch-transcode-video.svg)](https://github.com/nwronski/batch-transcode-video/) 6 | [![License Type Image](https://img.shields.io/github/license/nwronski/batch-transcode-video.svg)](https://github.com/nwronski/batch-transcode-video/blob/master/LICENSE) 7 | 8 | 9 | ![batch-transcode-video screenshot 1](https://cloud.githubusercontent.com/assets/5528249/11022434/025d9d4a-862d-11e5-8b0c-4134e7edd0d7.png) 10 | 11 | A command-line utility for recursively running batch cropping and transcoding operations on a directory of videos. This utility is a wrapper for a a utility called [transcode_video](https://github.com/donmelton/video_transcoding) which itself is a wrapper for trancoding utilities such as [HandbrakeCLI](https://handbrake.fr), [MKVToolNix](https://www.bunkus.org/videotools/mkvtoolnix/), and [ffmpeg](https://ffmpeg.org/). 12 | 13 | ## Prerequisites 14 | 15 | Requires [Node.js](https://nodejs.org) and [Ruby](https://www.ruby-lang.org). 16 | 17 | **Important:** You must have all of the dependencies listed in this section of [the transcode_video README](https://github.com/donmelton/video_transcoding#requirements). 18 | 19 | Before installing `batch-transcode-video`, install the `video_transcoding` gem using the following command. 20 | 21 | ``` 22 | gem install video_transcoding 23 | ``` 24 | 25 | ## Installation 26 | 27 | This utility can be installed so that it is globally accessible from the terminal as `batch-transcode-video` using the following command. 28 | 29 | ``` 30 | npm i batch-transcode-video -g 31 | ``` 32 | 33 | ## Usage 34 | 35 | For all the videos found in the `input` directory, this utility will determine if the source should be cropped (e.g.: it has black bars on the top and bottom of the source video) using `detect-crop` and then it will be transcoded using `transcode-video`. When in CLI mode, the progress and remaining time for the current file and the entire batch are displayed. 36 | 37 | This utility can recover from most errors, and as such, it will continue to **sequentially** process source files even if a previous transcoding operation has failed. 38 | 39 | A summary is displayed when the entire batch transcoding operation is finished. The summary includes the overall number of errors encountered and files successfully created. 40 | 41 | ### Before You Begin 42 | 43 | **WARNING:** If the program cannot successfully transcode an input file, and then verify the output video file, it will be marked as _errored_ and the transcoded file (if one exists) will be deleted from the output directory. This is done to prevent partially-transcoded files from remaining in the output directory that then need to be manually deleted before retrying the transcoding operation. 44 | 45 | If you run the program using an output directory containing previously-transcoded video, especially for source files that still exist in your input directory, you **must** use the `--diff` option to ensure sucessful transcodes from previous batches are not deleted from the output directory. In `--diff` mode, input files are skipped when their corresponding output files already exist in the output directory. 46 | 47 | Alternatively, you can use the `--keep` option to ensure files are never deleted from the output directory, even if they have errors or failed to finish transcoding. However, subsequent runs, with or without using the `--diff` option, will not reprocess failed input files, unless the corresponding output files are manually deleted. 48 | 49 | ### Instructions 50 | 51 | Recursively search for videos to transcode using [video_transcoding](https://github.com/donmelton/video_transcoding). If an `input` **directory** is not specified then the current directory will be used as the input directory. If an `output` **directory** is not specified, the `input` directory will be used for the transcoded videos. By default, if an output file already exists then it will be treated as an error. Use the `--diff` option to skip `input` files that already exist in the `output` folder. 52 | 53 | ``` 54 | batch-transcode-video --input video/ --output transcoded/ --diff 55 | ``` 56 | 57 | Transcoded files will be placed in the same directory as the source unless you specify an `output` directory. The relative folder structure will be maintained in the output directory unless you use the `flatten` flag. 58 | 59 | If you want to modify the search pattern that will be used to locate video in the `input` directory, you can specify a [glob](https://github.com/isaacs/node-glob) pattern using the `mask` option. 60 | 61 | ### Non-binary Usage 62 | 63 | You can also directly `require()` the underlying `BatchTranscodeVideo` video class. By default, the ES5-compatible files will be loaded when requiring this module. 64 | 65 | ``` javascript 66 | // ES5 (default) 67 | var BatchTranscodeVideo = require('batch-transcode-video'); 68 | var batch = new BatchTranscodeVideo({ 69 | input: './my/rawVideos/', 70 | output: './my/transcodedVideos/' 71 | }, ['--dry-run']); 72 | batch 73 | .transcodeAll() 74 | .then(function (res) { 75 | console.log(res); 76 | }) 77 | .catch(function (err) { 78 | console.log(err); 79 | }); 80 | ``` 81 | 82 | But, you can also require the raw ES2015 source files if you are running in an ES2015 capable environment. 83 | 84 | **Note:** The un-transpiled source files are not including when you install the library using `npm install`. To get the ES2015 source, you need to **clone this repository from GitHub** using `git clone`. 85 | 86 | ``` javascript 87 | // ES2015 88 | import BatchTranscodeVideo from './batch-transcode-video/index.js'; 89 | let batch = new BatchTranscodeVideo(options, transcodeOptions); 90 | ``` 91 | 92 | **Note:** If you `import` the non-CLI files, you will not see any formatted progress bars and summary output in the console. To require the CLI version of the library from your application, then simply `import` the `index-cli.js` file instead of `index.js`. 93 | 94 | ``` javascript 95 | import CliBatchTranscodeVideo from './batch-transcode-video/index-cli.js'; 96 | import minimist from 'minimist'; 97 | let options = minimist(process.argv.slice(2), {'--': true}); 98 | let batch = new CliBatchTranscodeVideo(options, options['--']); 99 | // Start CLI mode 100 | batch.cli(); 101 | ``` 102 | 103 | ## Options 104 | 105 | - `--help` _Flag: does not accept a value_ (Alias: `-h`) 106 | - You can view the manual for this tool by using this flag in the terminal. 107 | - `--input [path]` (Default: `process.cwd()`) (Alias: `-i`) 108 | - The input **directory** containing the source videos to transcode. 109 | - `--output [path]` (Default: _same directory as source files_) (Alias: `-o`) 110 | - The output **directory** to hold the transcoded videos. If you do not specify an output directory then each transcoded file will be placed in the same directory as its source file. Note: if a source file is already in the same file format as the transcoded video (e.g.: both source and output are both `.mkv`) then you must specify an output directory, as the program will not overwrite existing files. 111 | - `--mask [pattern]` (Default: `**/*.{mp4,avi,mkv,m4v,ts,mov}`) (Alias: `-m`) 112 | - Search pattern to use for input directory. Note that the default pattern will search in nested directories. For more information about what values can be used, see the [glob](https://github.com/isaacs/node-glob) documentation. 113 | - `--crop` _Mixed values_ (Alias: `-c`) 114 | - If you provide **an actual crop value** (e.g.: `"0:0:0:0"`) as the argument for this option, then that crop value will be used **for all videos**. 115 | - If you provide anything other than an actual crop value (e.g. `1`) as the argument for this option, then when crop detection returns conflicting crop values it will just use the least extreme crop value and continue transcoding. 116 | - `--diff` _Flag: does not accept a value_ (Default: `false`) 117 | - Enable this option if you only want to transcode source files that do not exist already in the `output` folder. 118 | - If a destination file already exists in the `output` directory: 119 | - And `diff` is **enabled**, a notice will be generated letting you know that the file was skipped (unless `quiet` is enabled). 120 | - And `diff` is **not enabled**, an error will be generated letting you know that the file already exists. 121 | - If you want to transcode a batch of videos in-place (i.e.: without specifying an `output` directory) then you should enabled this option to prevent errors from being generated when the source and destination file names have the same extension. 122 | - For example: trying to transcode a `.mkv` video into a `.mkv` video without supplying an external `output` directory will generate an error unless you specify the `diff` flag. 123 | - `--debug` _Flag: does not accept a value_ (Default: `false`) 124 | Enable verbose logging mode. Disables progress indicator and then streams child process to master `stdout` for `detect-crop` and `transcode-video`. 125 | - `--flatten` _Flag: does not accept a value_ (Default: `false`) 126 | - Do not preserve relative directory structure in output directory. If this is enabled, the base output folder will contain all transcoded videos. Note: this option has no effect unless you specify an `output` directory. 127 | - `--quiet` _Flag: does not accept a value_ (Default: `false`) 128 | - Prevents **any** logging to stdout and will only exit `0` on success or `1` on error 129 | - `--keep` _Flag: does not accept a value_ (Default: `false`) (Alias: `-k`) 130 | - **Never delete any output files**, no matter what happens, **even if the encoding task fails** for the corresponding input file. If you use this option, input files that fail to encode correctly, or finish encoding, will not be deleted from the output folder. Subsequent runs, with or without using the `--diff` option, will not reprocess the failed input files, unless the corresponding output files are manually deleted. 131 | - `--nocrop` _Flag: does not accept a value_ (Default: `false`) 132 | - Skip crop detection entirely (i.e., do not run `detect-crop`) and do not pass a `--crop` value to `transcode-video`. 133 | 134 | ### Providing options to transcode-video 135 | 136 | If you want to provide custom options to `trancode-video` then you can place them at the end of your normal options following a `--` and they will be passed directly to the `transcode-video` program. Find more information about the allowed options at [the transcode_video README](https://github.com/donmelton/video_transcoding#using-transcode-video). 137 | 138 | ``` 139 | batch-transcode-video --input video/ --output transcoded/ --diff -- --dry-run 140 | ``` 141 | -------------------------------------------------------------------------------- /batch-transcode-video.js: -------------------------------------------------------------------------------- 1 | import minimist from 'minimist'; 2 | import cli from '../dist/index-cli.js'; 3 | import defaultOptions from '../dist/lib/default-options.js'; 4 | const VERSION = '@@VERSION'; 5 | const UNKNOWN_MESSAGE = `If you would like to supply custom options to 6 | transcode-video then put them at end of the command 7 | after a double dash "--". For example to pass the 8 | "--dry-run" command to transcode-video:`.replace(/\n+/g, ''); 9 | let defs = { 10 | '--': true, 11 | alias: { 12 | input: 'i', 13 | output: 'o', 14 | mask: 'm', 15 | help: 'h', 16 | version: 'v', 17 | crop: 'c', 18 | keep: 'k' 19 | }, 20 | boolean: [ 21 | 'debug', 'quiet', 'flatten', 'diff', 'help', 'version', 'keep', 'nocrop' 22 | ], 23 | string: [ 24 | 'input', 'output', 'mask', 'crop' 25 | ], 26 | default: defaultOptions, 27 | unknown: function (arg) { 28 | console.log(`ERROR:\tUnrecognized argument ${arg} provided.`); 29 | console.log(UNKNOWN_MESSAGE); 30 | console.log('\nbatch-transcode-video --input my_videos/ -- --dry-run'); 31 | process.exit(1); 32 | } 33 | }; 34 | 35 | let options = minimist(process.argv.slice(2), defs); 36 | 37 | if (options['version'] === true) { 38 | console.log(`batch-transcode-video v${VERSION}`); 39 | process.exit(0); 40 | } 41 | 42 | let transcodeOptions = []; 43 | if (options['--'].length) { 44 | transcodeOptions = options['--'].slice(0); 45 | delete options['--']; 46 | } 47 | 48 | (new cli(options, transcodeOptions)).cli(); 49 | -------------------------------------------------------------------------------- /index-cli.js: -------------------------------------------------------------------------------- 1 | import Promise from 'bluebird'; 2 | import BatchTranscodeVideo from './index.js'; 3 | import Progress from './lib/progress.js'; 4 | import help from './lib/help.js'; 5 | import VideoFile from './lib/video-file.js'; 6 | import _charm from 'charm'; 7 | import ChildPromise from './lib/child-promise.js'; 8 | 9 | export default class CliBatchTranscodeVideo extends BatchTranscodeVideo { 10 | static get INTERVAL_MS() { return 1000; } 11 | static get FIRST_TAB() { return 10; } 12 | constructor(options, transcodeOptions) { 13 | super(options, transcodeOptions); 14 | this.timer = null; 15 | this.files = []; 16 | this.charm = _charm(process); 17 | if (this.options['help'] === true) { 18 | help(this.charm); 19 | process.exit(0); 20 | } 21 | this.progress = new Progress(this.charm, CliBatchTranscodeVideo.FIRST_TAB); 22 | ChildPromise.debug = this.options['debug']; 23 | return this; 24 | } 25 | 26 | cli() { 27 | process.on('uncaughtException', (err) => { 28 | this.error = err; 29 | this.onError(err); 30 | }); 31 | process.on('exit', () => { 32 | // Handle SIGINT 33 | if (!this.isDone) { 34 | this.status = BatchTranscodeVideo.ERRORED; 35 | } 36 | if (this.files && this.files.length) { 37 | for (let file of this.files) { 38 | file.kill(); 39 | } 40 | } 41 | this.finish(); 42 | }); 43 | 44 | if (this.options['quiet'] !== true) { 45 | this.progress.start(); 46 | } 47 | 48 | if (this.options['debug'] !== true && this.options['quiet'] !== true) { 49 | this.progress.write(this.state()); 50 | this.timer = setInterval(() => { 51 | let state = this.state(); 52 | this.progress.clear(); 53 | this.progress.write(state); 54 | }, CliBatchTranscodeVideo.INTERVAL_MS); 55 | } 56 | 57 | return this.transcodeAll() 58 | .then(res => this.onSuccess(res)) 59 | .catch(err => this.onError(err)); 60 | } 61 | 62 | finish() { 63 | if (this.timer !== null) { 64 | clearInterval(this.timer); 65 | this.timer = null; 66 | } 67 | if (this.options['quiet'] !== true) { 68 | // this.progress.clear(); 69 | this.progress.finish(); 70 | this.progress.summary(this.finalState()); 71 | } 72 | if (!this.isSuccess) { 73 | process.reallyExit(1); 74 | } 75 | } 76 | 77 | finalState() { 78 | let processed = this.processedFileSizes; 79 | let total = this.totalFileSizes; 80 | let seconds = this.totalTime / 1000.0; 81 | let speed = processed / seconds; 82 | let workCount = this.files.reduce((t, file) => { 83 | return t + file.currentPercent; 84 | }, 0); 85 | let average = workCount > 0 ? (this.totalTime / workCount) : 0; 86 | return { 87 | files: this.files, 88 | processed: processed.toFixed(2), 89 | total: total.toFixed(2), 90 | speed: speed.toFixed(2), 91 | success: this.isFinished, 92 | error: this.error, 93 | elapsed: this.totalTime, 94 | average 95 | }; 96 | } 97 | 98 | state() { 99 | try { 100 | let processed = this.processedFileSizes; 101 | let remaining = this.totalFileSizes.toFixed(2); 102 | return { 103 | current: { 104 | file: this.currentFile.fileName, 105 | percent: this.currentFile.currentPercent, 106 | elapsed: this.currentFile.currentTime, 107 | remaining: this.currentFile.remainingTime 108 | }, 109 | total: { 110 | percent: this.currentPercent, 111 | elapsed: this.currentTime, 112 | remaining: this.remainingTime, 113 | processed: `${processed > 0 ? processed.toFixed(2) : '(estimating)'} MB of ${remaining} MB`, 114 | files: this.files 115 | } 116 | }; 117 | } catch (e) { 118 | return { 119 | current: { 120 | file: `[Scanning ${this.files.length} files...]`, 121 | percent: 0, 122 | elapsed: 0, 123 | remaining: 0 124 | }, 125 | total: { 126 | percent: 0, 127 | elapsed: 0, 128 | remaining: 0, 129 | status: '[Calculating remaining time...]' 130 | } 131 | }; 132 | } 133 | } 134 | 135 | onSuccess(result) { 136 | process.exit(0); 137 | } 138 | 139 | onError(err) { 140 | process.exit(1); 141 | } 142 | }; 143 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Promise from 'bluebird'; 2 | import path from 'path'; 3 | import _glob from 'glob'; 4 | let glob = Promise.promisify(_glob); 5 | import {stat as _stat} from 'fs'; 6 | let stat = Promise.promisify(_stat); 7 | import TranscodeError from './lib/transcode-error.js'; 8 | import VideoFile from './lib/video-file.js'; 9 | import defaultOptions from './lib/default-options.js'; 10 | 11 | let destExtensionRegex = /^\-{2}(mp4|m4v)$/i; 12 | let dryRunRegex = /^\-\-dry\-run$/i; 13 | 14 | function sumFileSizes(files, useProperty = 'currentPercent') { 15 | return files.reduce(function (total, file) { 16 | return total + (file[useProperty] * file.fileSize); 17 | }, 0); 18 | } 19 | 20 | export default class BatchTranscodeVideo { 21 | static get INACTIVE() { return 0; } 22 | static get RUNNING() { return 1; } 23 | static get FINISHED() { return 2; } 24 | static get ERRORED() { return 3; } 25 | 26 | /* This is the value to use before there is any data to calculate speed 27 | * with estimateSpeed(). A value between 100 and 3000 seems reasonable. 28 | */ 29 | static get EST_MS_PER_MB() { return 1000.0; } 30 | 31 | constructor(options, transcodeOptions) { 32 | options['curDir'] = process.cwd(); 33 | options['input'] = path.relative(options['curDir'], options['input']); 34 | options['dryRun'] = transcodeOptions.length ? transcodeOptions.reduce(function (prev, cur) { 35 | return prev || dryRunRegex.test(cur.trim()); 36 | }, false) : false; 37 | options['destExt'] = transcodeOptions.reduce(function (prev, cur) { 38 | let curArg = cur.trim(); 39 | if (destExtensionRegex.test(curArg)) { 40 | return curArg.match(destExtensionRegex)[1]; 41 | } 42 | return prev; 43 | }, 'mkv'); 44 | this.filePattern = path.normalize(options['input'] + path.sep + options['mask']); 45 | this.options = Object.assign({}, defaultOptions, options); 46 | this.transcodeOptions = transcodeOptions.slice(0); 47 | this.status = BatchTranscodeVideo.INACTIVE; 48 | this.files = []; 49 | this.currentIndex = 0; 50 | this.error = null; 51 | this._ready = this.createEntries(); 52 | return this; 53 | } 54 | 55 | createEntries() { 56 | return glob(this.filePattern, {}) 57 | .then((files) => { 58 | if (files.length === 0) { 59 | throw new TranscodeError('No files found for search pattern provided.', this.filePattern); 60 | } 61 | return files; 62 | }, (err) => { 63 | throw new TranscodeError('File system error encountered while scanning for media.', this.filePattern, err.message); 64 | }) 65 | .map((file) => this.resolvePath(file), { 66 | concurrency: 3 67 | }) 68 | .then((files) => { 69 | this.files = files; 70 | return this.files; 71 | }); 72 | } 73 | 74 | transcodeAll() { 75 | return this._ready 76 | .then(() => { 77 | if (!this.isReady) { 78 | throw new TranscodeError('Batch has already been processed.', this.filePattern); 79 | } 80 | this.startTime = Date.now(); 81 | this.lastTime = this.startTime; 82 | this.status = BatchTranscodeVideo.RUNNING; 83 | return this.files; 84 | }) 85 | .mapSeries((video, index) => { 86 | this.lastTime = Date.now(); 87 | this.currentIndex = index; 88 | return video.transcode(); 89 | }) 90 | .then(() => { 91 | this.lastTime = Date.now(); 92 | this.stopTime = this.lastTime; 93 | this.status = BatchTranscodeVideo.FINISHED; 94 | this.currentIndex = -1; 95 | let errored = this.files.reduce((t, file) => t + (file.isErrored ? 1 : 0), 0); 96 | if (errored > 0) { 97 | this.status = BatchTranscodeVideo.ERRORED; 98 | } 99 | }) 100 | .catch((err) => { 101 | this.lastTime = Date.now(); 102 | this.stopTime = this.lastTime; 103 | this.status = BatchTranscodeVideo.ERRORED; 104 | this.error = err; 105 | throw err; 106 | }); 107 | } 108 | 109 | resolvePath(filePath) { 110 | return stat(filePath) 111 | .then((stats) => { 112 | return new VideoFile(filePath, stats, this.options, this.transcodeOptions, () => this.estimateSpeed()); 113 | }); 114 | } 115 | 116 | estimateSpeed() { 117 | let processed = sumFileSizes(this.files.slice(0, this.currentIndex), 'lastPercent'); 118 | if (processed > 0) { 119 | // ms/MB 120 | return (this.lastTime - this.startTime) / processed; 121 | } 122 | return BatchTranscodeVideo.EST_MS_PER_MB; 123 | } 124 | 125 | get processedFileSizes() { 126 | return sumFileSizes(this.files); 127 | } 128 | 129 | get totalFileSizes() { 130 | return this.files 131 | .reduce(function (total, file) { 132 | let useSize = file.fileSize; 133 | if (file.isErrored) { 134 | useSize *= file.currentPercent; 135 | } else if (file.isSkipped) { 136 | useSize = 0; 137 | } 138 | return total + useSize; 139 | }, 0); 140 | } 141 | 142 | get currentPercent() { 143 | return this.processedFileSizes / this.totalFileSizes; 144 | } 145 | 146 | get currentTime() { 147 | return (this.isRunning ? Date.now() : this.stopTime) - this.startTime; 148 | } 149 | 150 | get totalTime() { 151 | return this.isRunning ? 152 | (this.currentTime / this.currentPercent) : 153 | ((this.stopTime || Date.now()) - this.startTime); 154 | } 155 | 156 | get remainingTime() { 157 | return Math.max(this.totalTime - this.currentTime, 0); 158 | } 159 | 160 | get isReady() { 161 | return this.status === BatchTranscodeVideo.INACTIVE; 162 | } 163 | 164 | get isRunning() { 165 | return this.status === BatchTranscodeVideo.RUNNING; 166 | } 167 | 168 | get isDone() { 169 | return this.isFinished || this.isErrored; 170 | } 171 | 172 | get isFinished() { 173 | return this.status === BatchTranscodeVideo.FINISHED; 174 | } 175 | 176 | get isErrored() { 177 | return this.status === BatchTranscodeVideo.ERRORED; 178 | } 179 | 180 | get ready() { 181 | return this._ready; 182 | } 183 | 184 | get currentFile() { 185 | if (this.currentIndex >= 0) { 186 | return this.files[this.currentIndex]; 187 | } 188 | return null; 189 | } 190 | }; 191 | -------------------------------------------------------------------------------- /lib/child-promise.js: -------------------------------------------------------------------------------- 1 | import {isFunction} from './util.js'; 2 | import Promise from 'bluebird'; 3 | import spawn from 'cross-spawn-async'; 4 | import TranscodeError from './transcode-error.js'; 5 | let _debug = false; 6 | 7 | export default class ChildPromise { 8 | static get debug() { return _debug; } 9 | static set debug(val) { _debug = val; } 10 | constructor(options, childOptions) { 11 | this.options = Object.assign({ 12 | fileName: '', 13 | cmd: '', 14 | args: [], 15 | cwd: '.', 16 | onData: function () {}, 17 | onError: function () {}, 18 | // Note: Had to mute stderr to prevent false positive errors 19 | muted: true 20 | }, options); 21 | this.childOptions = Object.assign({ 22 | cwd: this.options.cwd 23 | }, childOptions); 24 | 25 | this.stdout = ''; 26 | this.stderr = ''; 27 | 28 | return this; 29 | } 30 | 31 | start() { 32 | this._promise = new Promise((a, r) => { 33 | this._accept = a; 34 | this._reject = r; 35 | let {cmd, args} = this.options; 36 | 37 | this._child = spawn(cmd, args, this.childOptions); 38 | 39 | this._child.stdout.on('data', data => this.dataHandler(data)); 40 | this._child.stderr.on('data', data => this.stdErrHandler(data)); 41 | this._child.on('close', code => this.closeHandler(code)); 42 | this._child.on('error', err => this.errHandler(err)); 43 | }); 44 | 45 | return this.promise; 46 | } 47 | 48 | dataHandler(buffer) { 49 | let data = buffer.toString().trim(); 50 | this.stdout += data; 51 | this.options.onData(data); 52 | if (ChildPromise.debug) { 53 | console.log(`stdio: ${data}`); 54 | } 55 | } 56 | 57 | stdErrHandler(buffer) { 58 | let data = buffer.toString(); 59 | this.stderr += data; 60 | this.options.onError(data); 61 | if (ChildPromise.debug) { 62 | console.log(`stderr: ${data}`); 63 | } 64 | } 65 | 66 | errHandler(err) { 67 | let additional = err.toString().trim(); 68 | this._reject(new TranscodeError('Child process encountered an error.', this.options.fileName, additional)); 69 | } 70 | 71 | closeHandler(code) { 72 | if (code !== 0 && this.options.muted !== true) { 73 | this.errHandler(this.stderr); 74 | } else { 75 | this._accept(this.stdout); 76 | } 77 | } 78 | 79 | kill() { 80 | try { 81 | this._child.kill(); 82 | } catch (e) {} 83 | } 84 | 85 | get promise() { 86 | return this._promise; 87 | } 88 | 89 | get child() { 90 | return this._child; 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /lib/default-options.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | let curDir = process.cwd(); 3 | export default { 4 | // Input folder 5 | input: curDir, 6 | // Output folder 7 | output: null, 8 | // Search pattern for glob in input directory 9 | mask: '**' + path.sep + '*.{mp4,avi,mkv,m4v,ts,mov,vob}', 10 | // Verbose logging 11 | debug: false, 12 | // Do not preserve relative directory structure in output directory 13 | flatten: false, 14 | // No progress or summary information logged to console. 15 | quiet: false, 16 | // Only try to transcode videos that do not exist in the output directory 17 | diff: false, 18 | // If crop detection returns multiple values then take the least extreme crop 19 | crop: false, 20 | // Never delete any output files, no matter what happens 21 | keep: false, 22 | // Skip crop detection and do not pass a --crop value to transcode-video 23 | nocrop: false, 24 | help: false 25 | }; 26 | -------------------------------------------------------------------------------- /lib/help.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import {splitter} from './util.js'; 3 | 4 | const args = [ 5 | { 6 | arg: 'input [path]', 7 | desc: 'The input directory containing the source videos to transcode.', 8 | def: 'current directory', 9 | alias: 'i' 10 | }, 11 | { 12 | arg: 'output [path]', 13 | desc: 'The output directory to hold the transcoded videos. If you do not specify an output directory then each transcoded file will be placed in the same directory as its source file. Note: if a source file is already in the same file format as the transcoded video (e.g.: both source and output are both .mkv) then you must specify an output directory, as the program will not overwrite existing files.', 14 | def: 'same as input', 15 | alias: 'o' 16 | }, 17 | { 18 | arg: 'mask [str]', 19 | desc: 'Search pattern to use for input directory. Note that the default pattern will search in nested directories. For more information about what values can be used, see the node-glob documentation.', 20 | def: `**${path.sep}*.{mp4,avi,mkv,m4v,ts,mov}`, 21 | alias: 'm' 22 | }, 23 | { 24 | arg: 'crop [crop]', 25 | desc: 'If you provide an actual crop value (e.g.: "0:0:0:0") as the argument for this option, then that crop value will be used for all videos. If you provide anything other than an actual crop value (e.g. 1) as the argument for this option, then when crop detection returns conflicting crop values it will just use the least extreme crop value and continue transcoding.', 26 | def: 'false', 27 | alias: 'c' 28 | }, 29 | { 30 | arg: 'diff', 31 | desc: 'Enable this option if you only want to transcode source files that do not exist already in the output folder.' 32 | }, 33 | { 34 | arg: 'debug', 35 | desc: 'Enable verbose logging mode. Will allow you to see the output from the child processes spawned for detect-crop and transcode-video.' 36 | }, 37 | { 38 | arg: 'flatten', 39 | desc: 'Do not preserve relative directory structure in output directory. If this is enabled, the base output folder will contain all transcoded videos. Note: this option has no effect unless you specify an output directory.' 40 | }, 41 | { 42 | arg: 'quiet', 43 | desc: 'Do not log output messages to command line, only exit 0 if successful or 1 if there are errors. This will disable the progress bars that display the current progress and remaining time estimates and also the summary output (writes, errors, stats) at end of process.' 44 | }, 45 | { 46 | arg: 'keep', 47 | desc: 'Never delete any output files, no matter what happens, even if the encoding task fails for the corresponding input file. If you use this option, input files that fail to encode correctly, or finish encoding, will not be deleted from the output folder. Subsequent runs, with or without using the --diff option, will not reprocess the failed input files, unless the corresponding output files are manually deleted.', 48 | alias: 'k' 49 | }, 50 | { 51 | arg: 'nocrop', 52 | desc: 'Skip crop detection entirely (i.e., do not run detect-crop) and do not pass a --crop value to transcode-video.' 53 | } 54 | ]; 55 | 56 | export default function help(charm) { 57 | charm 58 | .display('bright') 59 | .write('batch-transcode-video manual\n\n'); 60 | 61 | for (let arg of args) { 62 | printArg(arg, charm); 63 | } 64 | }; 65 | 66 | function printArg({arg, desc = false, def = false, alias = false}, charm) { 67 | charm 68 | .display('reset') 69 | .display('bright') 70 | .foreground('yellow') 71 | .write(`--${arg}`) 72 | .display('reset'); 73 | if (def) { 74 | charm 75 | .display('reset') 76 | .foreground('blue') 77 | .write(`\t[${def}]`) 78 | .display('reset'); 79 | } 80 | if (alias) { 81 | charm 82 | .display('reset') 83 | .write(` (Alias: `) 84 | .display('bright') 85 | .foreground('yellow') 86 | .write(`-${alias}`) 87 | .display('reset') 88 | .write(`)`); 89 | } 90 | charm 91 | .write('\n'); 92 | if (desc) { 93 | charm 94 | .display('reset') 95 | .foreground('white') 96 | .write(`${splitter(desc)}\n`) 97 | .display('reset'); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/progress.js: -------------------------------------------------------------------------------- 1 | import {millisecondsToStr as ms2Str, fractionToPercent as f2Percent} from './util.js'; 2 | 3 | export default class Progress { 4 | constructor(charm, firstTab) { 5 | this.charm = charm; 6 | this.firstTab = firstTab; 7 | this.firstPad = ' '.repeat(this.firstTab); 8 | this.color = { 9 | skipped: 'blue', 10 | written: 'green', 11 | errored: 'red', 12 | queued: 'yellow' 13 | }; 14 | this.bold = { 15 | skipped: false, 16 | written: false, 17 | errored: true, 18 | queued: true 19 | }; 20 | } 21 | 22 | start() { 23 | this.charm 24 | .display('bright') 25 | .foreground('cyan') 26 | .write('\nStarting batch operation...') 27 | .display('reset') 28 | .write('\n\n'); 29 | } 30 | 31 | finish() { 32 | this.charm 33 | .display('bright') 34 | .foreground('cyan') 35 | .write('Finished processing.') 36 | .display('reset') 37 | .write('\n\n'); 38 | } 39 | 40 | summary(state) { 41 | if (state.error !== null) { 42 | this.charm 43 | .display('reset') 44 | .foreground('white') 45 | .write(`\n${state.error}\n`); 46 | } 47 | let counts = Progress.getCounts(state.files, true); 48 | for (let file of state.files) { 49 | let type = Progress.getStatusName(file, true); 50 | let bitrate = file.encodeBitrate !== null ? ` (${file.encodeBitrate})` : ''; 51 | this.bulletPoint(`${Progress.labelPad(`${type.toUpperCase()}: `, 10)}${Progress.truncateStr(file.fileName, 0.5, 0)}${bitrate}`, this.color[type], this.bold[type]); 52 | if (file.error !== null) { 53 | // Print error message 54 | this.charm 55 | .display('reset') 56 | .foreground('white') 57 | .write(`${file.error}\n`); 58 | } 59 | } 60 | this.charm.write(`\n`); 61 | this.colorBar(`Summary`, counts, `${state.files.length} file${state.files.length !== 1 ? 's' : ''}`); 62 | this.fileStatusLine(counts); 63 | this.truncatedLine('Processed', `${state.processed} MB of ${state.total} total MB`, `${state.speed} MB/s`); 64 | this.truncatedLine('Time', `Total ${ms2Str(state.elapsed)} (Avg: ${ms2Str(state.average)})`); 65 | this.charm.write(`\n`); 66 | let finalMsg = state.success ? `Finished without error.` : `Finished with errors.`; 67 | this.bulletPoint(finalMsg, (state.success ? 'green' : 'red'), true, '=>'); 68 | this.charm.display('reset').write(`\n`); 69 | } 70 | 71 | write(state) { 72 | let cur = state.current; 73 | let total = state.total; 74 | let counts = Progress.getCounts(total.files, false); 75 | this.bar(`Current`, cur.percent); 76 | this.truncatedLine('File', cur.file, ms2Str(cur.remaining)); 77 | this.charm.write(`\n`); 78 | this.bar(`Total`, total.percent); 79 | this.truncatedLine('Processed', total.processed, ms2Str(total.remaining)); 80 | this.fileStatusLine(counts); 81 | this.charm.display('reset').write(`\n`); 82 | } 83 | 84 | fileStatusLine(counts) { 85 | this.charm 86 | .display('reset') 87 | .foreground('cyan') 88 | .write(`${this.firstPad}${Progress.labelPad(`Status: `)}`); 89 | counts['queued'] = Math.max(counts['queued'] - 1, 0); 90 | for (let type of Object.keys(counts).filter(c => counts[c] > 0)) { 91 | this.charm 92 | .display('reset') 93 | .display('bright') 94 | .foreground(this.color[type]) 95 | .write(`${type.slice(0, 2).toUpperCase()}: `) 96 | .display('reset') 97 | .write(`${counts[type]} `); 98 | } 99 | this.charm.display('reset').write('\n'); 100 | } 101 | 102 | bulletPoint(line, color, bold = false, dash = '-') { 103 | this.charm 104 | .display(bold ? 'bright' : 'reset') 105 | .foreground(color) 106 | .write(` ${dash} ${line}\n`); 107 | } 108 | 109 | truncatedLine(label, str = '', extra = '', size = 0.5, labelLen = 8) { 110 | labelLen = Math.max(labelLen, label.length + 2); 111 | let formatted = Progress.truncateStr(str, size, labelLen); 112 | this.charm 113 | .display('reset') 114 | .foreground('cyan') 115 | .write(`${this.firstPad}${Progress.labelPad(`${label}: `, labelLen)}`) 116 | .display('reset') 117 | .foreground('white') 118 | .write(`${formatted}`) 119 | .foreground('yellow') 120 | .write(` ${extra}\n`); 121 | } 122 | 123 | bar(label, percent, size = 0.5) { 124 | let printPercent = f2Percent(percent); 125 | let colored = Math.round(Number.parseFloat(printPercent) * size); 126 | let uncolored = (100.0 * size) - colored; 127 | this.charm 128 | .display('bright') 129 | .foreground('cyan') 130 | .write(Progress.labelPad(`${label}: `, this.firstTab)) 131 | .display('reset') 132 | .background('cyan') 133 | .write(' '.repeat(colored)) 134 | .display('reset') 135 | .background('black') 136 | .write(' '.repeat(uncolored)) 137 | .display('reset') 138 | .display('bright') 139 | .write(` ${printPercent}%\n`); 140 | } 141 | 142 | colorBar(label, counts, extra = '', size = 0.5) { 143 | let types = Object.keys(counts).filter((key) => counts[key] > 0); 144 | let totalCount = types.reduce((total, type) => { 145 | return total + counts[type]; 146 | }, 0); 147 | this.charm 148 | .display('bright') 149 | .foreground('cyan') 150 | .write(Progress.labelPad(`${label}: `, this.firstTab)); 151 | let total = 100.0 * size; 152 | if (types.length !== 0) { 153 | for (let type of types) { 154 | let percent = counts[type] / totalCount; 155 | let printPercent = f2Percent(percent); 156 | let colored = Math.round(Number.parseFloat(printPercent) * size); 157 | if (type === types.slice(-1)[0]) { 158 | colored = total; 159 | } 160 | total -= colored; 161 | this.charm 162 | .display('reset') 163 | .background(this.color[type]) 164 | .write(' '.repeat(colored)); 165 | } 166 | } else { 167 | this.charm 168 | .display('reset') 169 | .background('black') 170 | .write(' '.repeat(total)); 171 | } 172 | this.charm 173 | .display('reset') 174 | .display('bright') 175 | .write(` ${extra}\n`); 176 | } 177 | 178 | clear() { 179 | this.charm.up(7).erase('line').erase('down').write('\r'); 180 | } 181 | 182 | static getCounts(files = [], done = false) { 183 | let counts = { 184 | written: 0, 185 | errored: 0, 186 | skipped: 0, 187 | queued: 0 188 | }; 189 | files.forEach((file) => { 190 | let type = Progress.getStatusName(file, done); 191 | counts[type] += 1; 192 | }); 193 | return counts; 194 | } 195 | 196 | static getStatusName(file, done = false) { 197 | let type; 198 | if (file.isWritten) { 199 | type = 'written'; 200 | } else if (file.isErrored || (done && file.isRunning)) { 201 | type = 'errored'; 202 | } else if (file.isSkipped) { 203 | type = 'skipped' 204 | } else if (file.isReady || (!done && file.isRunning)) { 205 | type = 'queued'; 206 | } 207 | return type; 208 | } 209 | 210 | static truncateStr(str, size = 0.5, labelLen = 8) { 211 | let maxLen = 100.0 * size; 212 | let maxWithoutLabel = maxLen - labelLen; 213 | let formatted; 214 | if (str.length > maxWithoutLabel) { 215 | let frontStr = str.slice(0, maxWithoutLabel - 13); 216 | let endStr = str.slice(-10); 217 | formatted = `${frontStr}...${endStr}`; 218 | } else { 219 | let paddingSize = maxWithoutLabel - str.length; 220 | let padding = ' '.repeat(paddingSize); 221 | formatted = `${str}${padding}`; 222 | } 223 | return formatted; 224 | } 225 | 226 | static labelPad(label, pad = 8) { 227 | return label.length < pad ? label + (' '.repeat(pad - label.length)) : label; 228 | } 229 | }; 230 | -------------------------------------------------------------------------------- /lib/transcode-error.js: -------------------------------------------------------------------------------- 1 | export default class TranscodeError extends Error { 2 | constructor(message, file, additional = '') { 3 | super(); 4 | this._message = message; 5 | this.file = file; 6 | this.additional = additional; 7 | // If we get ENOENT then we might be missing the video_transcoding gem 8 | if (/ENOENT/i.test('' + this.additional)) { 9 | this.additional += '\nHave you run "gem install video_transcoding"?'; 10 | } 11 | this.message = [`[TranscodeError: ${this._message}]`, `File: ${this.file}`, this.additional].join("\n"); 12 | } 13 | 14 | toString() { 15 | return this.message; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | export function isFunction(obj) { 2 | return type(obj) === 'function'; 3 | }; 4 | 5 | export function isString(obj) { 6 | return type(obj) === 'string'; 7 | }; 8 | 9 | export function type(obj) { 10 | let typeMatches = Object.prototype.toString.call(obj).match(/\[object\s([a-z]+)\]/i); 11 | return typeMatches != null ? typeMatches[1].toLowerCase() : null; 12 | }; 13 | 14 | export function strToMilliseconds(strTime) { 15 | let [_, h, m, s] = strTime.toString().match(/([0-9]{2})\:([0-9]{2})\:([0-9]{2})/); 16 | return ((h * Math.pow(60, 3)) + (m * Math.pow(60, 2)) + (s * 60)) * 1000; 17 | }; 18 | 19 | export function millisecondsToStr(ms) { 20 | let parts = [0, 0, 0]; 21 | let partLabels = ['h', 'm', 's'] 22 | if (isANumber(ms) && ms > 1000) { 23 | ms /= 1000; 24 | parts = [ 25 | Math.floor(ms / Math.pow(60, 2)), 26 | Math.floor((ms % Math.pow(60, 2)) / 60), 27 | Math.round(ms % 60) 28 | ]; 29 | } 30 | return parts.map((p, i) => `${padTo(p.toString())}${partLabels[i]}`).join(' '); 31 | }; 32 | 33 | export function fractionToPercent(frac) { 34 | let [min, max] = [0.0, 100.0]; 35 | let percent = isANumber(frac) ? Math.max(Math.min(max * frac, max), min) : min; 36 | return padTo(percent.toPrecision(5), '0', 6, 0); 37 | }; 38 | 39 | export function isANumber(num) { 40 | return !Number.isNaN(Number.parseFloat(num)) && isFinite(num); 41 | }; 42 | 43 | // dir: 0 = right, 1 = left 44 | export function padTo(str, char = '0', count = 2, dir = 1) { 45 | let right = dir === 0; 46 | let pad = char.repeat(count); 47 | let chars = right ? `${str}${pad}` : `${pad}${str}`; 48 | return right ? chars.slice(0, count) : chars.slice(-1 * count); 49 | }; 50 | 51 | export function splitter(str, left, len) { 52 | const MAX_LENGTH = len || 40; 53 | let words = str.split(' '), built = '', line = [], lineLen = 0; 54 | function makeLine() { 55 | return (!left ? '\t\t' : '') + line.join(' ') + '\n'; 56 | } 57 | function addLine() { 58 | if (lineLen > 0) { 59 | built += makeLine(); 60 | lineLen = 0; 61 | line = []; 62 | } 63 | } 64 | function extendLine(w) { 65 | lineLen += w.length; 66 | line.push(w); 67 | } 68 | words.forEach(function (word) { 69 | if ((lineLen + word.length) > MAX_LENGTH) { 70 | addLine(); 71 | } 72 | extendLine(word); 73 | }); 74 | addLine(); 75 | return built; 76 | }; 77 | -------------------------------------------------------------------------------- /lib/video-file.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import Promise from 'bluebird'; 3 | import TranscodeError from './transcode-error.js'; 4 | import {stat as _stat, unlinkSync} from 'fs'; 5 | let stat = Promise.promisify(_stat); 6 | import _mkdirp from 'mkdirp'; 7 | let mkdirp = Promise.promisify(_mkdirp); 8 | import ChildPromise, {windowsCommand} from './child-promise.js'; 9 | import {parse} from 'shell-quote'; 10 | import {strToMilliseconds as strToMs} from './util.js'; 11 | 12 | let progressPattern = 'Encoding: task'; 13 | let progressPercent = /(\d{1,3}\.\d{1,2})\s*\%/; 14 | let timePattern = '([0-9]{2}\:[0-9]{2}\:[0-9]{2})'; 15 | let handbrakeLogTime = new RegExp(`^${timePattern}`); 16 | let handbrakeLogBitrate = /[0-9]+\.[0-9]+\s[^\s]+/i; 17 | let handbrakeFinish = new RegExp('Encode done!', 'mi'); 18 | let handbrakeRuntime = new RegExp(`Elapsed time:\\s+${timePattern}`, 'mi'); 19 | let cropValuePattern = /\-{2}crop\s+([0-9]+\:[0-9]+\:[0-9]+\:[0-9]+)/i; 20 | function cropDelta(command) { 21 | let cropRaw = command.match(cropValuePattern); 22 | if (cropRaw === null) { return null; } 23 | return cropRaw[1] 24 | .split(':') 25 | .reduce((t, val) => t + Number.parseInt(val, 10), 0); 26 | } 27 | 28 | function cropCompareFunc(a, b) { 29 | let cropA = cropDelta(a); 30 | let cropB = cropDelta(b); 31 | if (cropA === null) { 32 | return 1; 33 | } else if (cropB === null) { 34 | return -1; 35 | } 36 | return cropA >= cropB ? 1 : -1; 37 | } 38 | 39 | export default class VideoFile { 40 | static get QUEUED() { return 0; } 41 | static get RUNNING() { return 1; } 42 | static get WRITTEN() { return 2; } 43 | static get ERRORED() { return 3; } 44 | static get SKIPPED() { return 4; } 45 | 46 | constructor(filePath, stats, options, transcodeOptions = [], estimator = function () { return null; }) { 47 | this.getEstSpeed = estimator; 48 | this.options = options; 49 | this.transcodeOptions = transcodeOptions; 50 | this.status = VideoFile.QUEUED; 51 | this.shouldDelete = false; 52 | 53 | this.lastPercent = 0; 54 | 55 | this._crop = null; 56 | this._encode = null; 57 | this._query = null; 58 | 59 | this.error = null; 60 | this.encodeBitrate = null; 61 | 62 | this.fileName = path.basename(filePath); 63 | this.filePathDir = path.dirname(filePath); 64 | this.filePathRel = path.relative(this.options['curDir'], filePath); 65 | this.destFileName = path.basename(this.fileName, path.extname(this.fileName)) + '.' + this.options['destExt']; 66 | this.destFileDir = this.options['output'] ? (!this.options['flatten'] ? 67 | // Add relative paths from --input to filePathDir when --o given 68 | path.resolve(this.options['output'], path.relative(this.options['input'], this.filePathDir)) : 69 | // --flatten option so do not add relative path 70 | this.options['output']) : 71 | // Output is same place a input 72 | this.filePathDir; 73 | this.destFilePath = path.normalize(this.destFileDir + path.sep + this.destFileName); 74 | this.destFilePathRel = path.relative(this.options['curDir'], this.destFilePath); 75 | this.fileSize = Number.parseInt(stats.size / 1000000.0, 10); 76 | this._ready = this._resolveDest(); 77 | return this; 78 | } 79 | 80 | transcode() { 81 | return this._ready 82 | .then(() => { 83 | if (!this.isReady) { 84 | throw new TranscodeError('File cannot be processed again.', this.fileName); 85 | } else if (this.destFileExists) { 86 | if (this.options['diff']) { 87 | this.status = VideoFile.SKIPPED; 88 | return Promise.resolve(false); 89 | } else { 90 | throw new TranscodeError('File already exists in output directory.', this.fileName); 91 | } 92 | } else { 93 | this.startTime = Date.now(); 94 | this.lastTime = this.startTime; 95 | this.status = VideoFile.RUNNING; 96 | 97 | return this._detectCrop() 98 | .then(args => this._startEncode(args)) 99 | .then(didFinish => this._encodeStatus(didFinish)) 100 | .then(() => { 101 | this.lastPercent = 1.0; 102 | this.status = VideoFile.WRITTEN; 103 | return true; 104 | }); 105 | } 106 | }) 107 | .catch((e) => { 108 | this.error = e; 109 | this.status = VideoFile.ERRORED; 110 | }); 111 | } 112 | 113 | kill() { 114 | if (this._encode !== null) { 115 | this._encode.kill(); 116 | } 117 | // Handle SIGINT 118 | if (this.isRunning) { 119 | this.status = VideoFile.ERRORED; 120 | } 121 | try { 122 | if (this.isMarkedForDeletion) { 123 | // Try and delete the destination file if it exists 124 | unlinkSync(this.destFilePath); 125 | } 126 | } catch (e) {} 127 | } 128 | 129 | _detectCrop() { 130 | let prom; 131 | let isNoCrop = this.options['nocrop'] === true; 132 | let isFixedCrop = /([0-9]+\:){3}[0-9]+/.test(this.options['crop']); 133 | if (isNoCrop || isFixedCrop) { 134 | const useCommand = [ 'transcode-video' ]; 135 | if (isFixedCrop && !isNoCrop) { 136 | // Force the crop value provided using the --crop option 137 | useCommand.push('--crop', this.options['crop'].trim()); 138 | } 139 | useCommand.push(this.filePathRel); 140 | this._crop = prom = Promise.resolve(useCommand.join(' ')); 141 | } else { 142 | this._crop = new ChildPromise({ 143 | cmd: 'detect-crop', 144 | args: [this.filePathRel], 145 | fileName: this.fileName, 146 | cwd: this.options['curDir'] 147 | }); 148 | prom = this._crop.start(); 149 | } 150 | return prom 151 | .then((output) => { 152 | // Make sure conflicting results are not returned from detect-crop 153 | let useCommands = output 154 | .replace(/^\s+|\s+$/g, '') 155 | .split(/\n+/) 156 | .map((line) => line.trim()) 157 | .filter((line) => /^transcode\-video/.test(line)); 158 | if (useCommands.length === 0) { 159 | throw new TranscodeError('Crop detection failed. Skipping transcode for file.', this.fileName, output); 160 | } else if (useCommands.length > 1) { 161 | if (this.options['crop'] !== false) { 162 | // Pick the least extreme crop 163 | useCommands.sort(cropCompareFunc); 164 | } else { 165 | let cropResults = useCommands.map(val => { 166 | let m = val.match(cropValuePattern); 167 | return m !== null ? m[1] : '(unknown)'; 168 | }).join(', '); 169 | throw new TranscodeError(`Crop detection returned conflicting results: ${cropResults}.`, this.fileName, useCommands.join('\n')); 170 | } 171 | } 172 | return useCommands[0]; 173 | }) 174 | .then((command) => { 175 | let useArgs = parse(command); 176 | useArgs.splice(1, 0, this.filePathRel, '--output', this.destFileDir); 177 | useArgs.splice.apply(useArgs, [useArgs.length - 1, 0].concat(this.transcodeOptions)); 178 | let crop = useArgs.indexOf('--crop') + 1; 179 | if (crop > 0) { 180 | this.cropValue = useArgs[crop]; 181 | } else if (this.options['nocrop'] !== true) { 182 | throw new TranscodeError('Could not detect crop values. Skipping transcode for file.', this.fileName, command); 183 | } 184 | return useArgs; 185 | }); 186 | } 187 | 188 | _startEncode([cmd, ...args]) { 189 | // This step is the earliest we would have a partially-encoded, new file in the destination directory 190 | this.shouldDelete = true; 191 | this._encode = new ChildPromise({ 192 | cmd, 193 | args, 194 | fileName: this.destFileName, 195 | cwd: this.options['curDir'], 196 | onData: (data) => { 197 | let lastIndex = data.lastIndexOf(progressPattern); 198 | if (lastIndex !== -1) { 199 | let lastData = data.substr(lastIndex); 200 | if (progressPercent.test(lastData)) { 201 | let matches = lastData.match(progressPercent); 202 | this.lastPercent = Number.parseFloat(matches[1]) / 100.0; 203 | this.lastTime = Date.now(); 204 | } 205 | } 206 | } 207 | }); 208 | return this._encode.start() 209 | .then((output) => { 210 | // Get total running time 211 | if (this.options['dryRun']) { 212 | return false; 213 | } else { 214 | // Check the output from the transcode to confirm it finished 215 | if (!handbrakeFinish.test(output)) { 216 | throw new TranscodeError('Transcode probably did not succeed for file.', this.destFileName, output); 217 | } 218 | this.totalEncodeTime = null; 219 | this.lastTime = Date.now(); 220 | this.lastPercent = 1.0; 221 | let transcodeRuntime = output.match(handbrakeRuntime); 222 | if (transcodeRuntime != null) { 223 | this.totalEncodeTime = strToMs(transcodeRuntime[1]); 224 | } else if (this.options['debug']) { 225 | console.log(`unable to determine running time from transcode log: ${this.destFileName}`); 226 | } 227 | return true; 228 | } 229 | }); 230 | } 231 | 232 | _encodeStatus(didFinish) { 233 | if (!didFinish) { 234 | return Promise.resolve(true); 235 | } 236 | this._query = new ChildPromise({ 237 | cmd: 'query-handbrake-log', 238 | args: ['bitrate', `${this.destFilePath}.log`], 239 | fileName: this.destFileName, 240 | cwd: this.options['curDir'] 241 | }); 242 | return this._query.start() 243 | .then((log) => { 244 | let matches = `${log}`.trim().match(handbrakeLogBitrate); 245 | this.encodeBitrate = matches[0]; 246 | return true; 247 | }) 248 | .catch((err) => { 249 | // We failed to get bitrate from the log, but that doesn't mean we failed 250 | if (this.options['debug']) { 251 | console.log(`unable to get bitrate from encoding log: ${err.message}`); 252 | } 253 | this.encodeBitrate = null; 254 | return true; 255 | }); 256 | } 257 | 258 | _resolveDest() { 259 | return stat(this.destFilePathRel) 260 | .then(() => { 261 | this.destFileExists = true; 262 | return true; 263 | }, () => { 264 | this.destFileExists = false; 265 | return mkdirp(this.destFileDir, {}); 266 | }); 267 | } 268 | 269 | get currentPercent() { 270 | if (!this.isRunning) { 271 | return this.lastPercent; 272 | } else if (this.lastPercent <= 0) { 273 | // Determine whether we should guess 274 | if (this.isRunning) { 275 | let est = this.getEstSpeed(); 276 | return (this.currentTime / est) / this.fileSize; 277 | } 278 | return 0; 279 | } 280 | return this.currentTime / this.totalTime; 281 | } 282 | 283 | get currentTime() { 284 | return Date.now() - this.startTime; 285 | } 286 | 287 | get totalTime() { 288 | return (this.lastTime - this.startTime) / this.lastPercent; 289 | } 290 | 291 | get remainingTime() { 292 | if (this.lastPercent > 0) { 293 | return Math.max(this.totalTime - this.currentTime, 0); 294 | } 295 | if (this.isRunning) { 296 | let est = this.getEstSpeed(); 297 | return (this.fileSize * est) - this.currentTime; 298 | } 299 | return 0; 300 | } 301 | 302 | get isReady() { 303 | return this.status === VideoFile.QUEUED; 304 | } 305 | 306 | get isRunning() { 307 | return this.status === VideoFile.RUNNING; 308 | } 309 | 310 | get isSkipped() { 311 | return this.status === VideoFile.SKIPPED; 312 | } 313 | 314 | get isWritten() { 315 | return this.status === VideoFile.WRITTEN; 316 | } 317 | 318 | get isErrored() { 319 | return this.status === VideoFile.ERRORED; 320 | } 321 | 322 | get isFinished() { 323 | return this.isWritten || this.isErrored || this.isSkipped; 324 | } 325 | 326 | get isMarkedForDeletion() { 327 | return this.isErrored && this.shouldDelete && this.options['keep'] !== true; 328 | } 329 | }; 330 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batch-transcode-video", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 11 | "dev": true 12 | }, 13 | "ansi-regex": { 14 | "version": "2.1.1", 15 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 16 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 17 | "dev": true 18 | }, 19 | "ansi-styles": { 20 | "version": "2.2.1", 21 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 22 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 23 | "dev": true 24 | }, 25 | "applause": { 26 | "version": "1.2.2", 27 | "resolved": "https://registry.npmjs.org/applause/-/applause-1.2.2.tgz", 28 | "integrity": "sha1-qEaFeegfZzl7tWNMKZU77c0PVsA=", 29 | "dev": true, 30 | "requires": { 31 | "cson-parser": "^1.1.0", 32 | "js-yaml": "^3.3.0", 33 | "lodash": "^3.10.0" 34 | }, 35 | "dependencies": { 36 | "lodash": { 37 | "version": "3.10.1", 38 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", 39 | "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", 40 | "dev": true 41 | } 42 | } 43 | }, 44 | "argparse": { 45 | "version": "1.0.9", 46 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 47 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 48 | "dev": true, 49 | "requires": { 50 | "sprintf-js": "~1.0.2" 51 | } 52 | }, 53 | "array-differ": { 54 | "version": "1.0.0", 55 | "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", 56 | "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", 57 | "dev": true 58 | }, 59 | "array-find-index": { 60 | "version": "1.0.2", 61 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 62 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", 63 | "dev": true 64 | }, 65 | "array-union": { 66 | "version": "1.0.2", 67 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 68 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 69 | "dev": true, 70 | "requires": { 71 | "array-uniq": "^1.0.1" 72 | } 73 | }, 74 | "array-uniq": { 75 | "version": "1.0.3", 76 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 77 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 78 | "dev": true 79 | }, 80 | "arrify": { 81 | "version": "1.0.1", 82 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 83 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 84 | "dev": true 85 | }, 86 | "async": { 87 | "version": "1.5.2", 88 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 89 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 90 | "dev": true 91 | }, 92 | "babel-code-frame": { 93 | "version": "6.26.0", 94 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 95 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 96 | "dev": true, 97 | "requires": { 98 | "chalk": "^1.1.3", 99 | "esutils": "^2.0.2", 100 | "js-tokens": "^3.0.2" 101 | } 102 | }, 103 | "babel-core": { 104 | "version": "6.26.0", 105 | "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", 106 | "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", 107 | "dev": true, 108 | "requires": { 109 | "babel-code-frame": "^6.26.0", 110 | "babel-generator": "^6.26.0", 111 | "babel-helpers": "^6.24.1", 112 | "babel-messages": "^6.23.0", 113 | "babel-register": "^6.26.0", 114 | "babel-runtime": "^6.26.0", 115 | "babel-template": "^6.26.0", 116 | "babel-traverse": "^6.26.0", 117 | "babel-types": "^6.26.0", 118 | "babylon": "^6.18.0", 119 | "convert-source-map": "^1.5.0", 120 | "debug": "^2.6.8", 121 | "json5": "^0.5.1", 122 | "lodash": "^4.17.4", 123 | "minimatch": "^3.0.4", 124 | "path-is-absolute": "^1.0.1", 125 | "private": "^0.1.7", 126 | "slash": "^1.0.0", 127 | "source-map": "^0.5.6" 128 | } 129 | }, 130 | "babel-generator": { 131 | "version": "6.26.1", 132 | "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", 133 | "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", 134 | "dev": true, 135 | "requires": { 136 | "babel-messages": "^6.23.0", 137 | "babel-runtime": "^6.26.0", 138 | "babel-types": "^6.26.0", 139 | "detect-indent": "^4.0.0", 140 | "jsesc": "^1.3.0", 141 | "lodash": "^4.17.4", 142 | "source-map": "^0.5.7", 143 | "trim-right": "^1.0.1" 144 | }, 145 | "dependencies": { 146 | "jsesc": { 147 | "version": "1.3.0", 148 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", 149 | "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", 150 | "dev": true 151 | } 152 | } 153 | }, 154 | "babel-helper-builder-binary-assignment-operator-visitor": { 155 | "version": "6.24.1", 156 | "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", 157 | "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", 158 | "dev": true, 159 | "requires": { 160 | "babel-helper-explode-assignable-expression": "^6.24.1", 161 | "babel-runtime": "^6.22.0", 162 | "babel-types": "^6.24.1" 163 | } 164 | }, 165 | "babel-helper-call-delegate": { 166 | "version": "6.24.1", 167 | "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", 168 | "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", 169 | "dev": true, 170 | "requires": { 171 | "babel-helper-hoist-variables": "^6.24.1", 172 | "babel-runtime": "^6.22.0", 173 | "babel-traverse": "^6.24.1", 174 | "babel-types": "^6.24.1" 175 | } 176 | }, 177 | "babel-helper-define-map": { 178 | "version": "6.26.0", 179 | "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", 180 | "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", 181 | "dev": true, 182 | "requires": { 183 | "babel-helper-function-name": "^6.24.1", 184 | "babel-runtime": "^6.26.0", 185 | "babel-types": "^6.26.0", 186 | "lodash": "^4.17.4" 187 | } 188 | }, 189 | "babel-helper-explode-assignable-expression": { 190 | "version": "6.24.1", 191 | "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", 192 | "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", 193 | "dev": true, 194 | "requires": { 195 | "babel-runtime": "^6.22.0", 196 | "babel-traverse": "^6.24.1", 197 | "babel-types": "^6.24.1" 198 | } 199 | }, 200 | "babel-helper-function-name": { 201 | "version": "6.24.1", 202 | "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", 203 | "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", 204 | "dev": true, 205 | "requires": { 206 | "babel-helper-get-function-arity": "^6.24.1", 207 | "babel-runtime": "^6.22.0", 208 | "babel-template": "^6.24.1", 209 | "babel-traverse": "^6.24.1", 210 | "babel-types": "^6.24.1" 211 | } 212 | }, 213 | "babel-helper-get-function-arity": { 214 | "version": "6.24.1", 215 | "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", 216 | "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", 217 | "dev": true, 218 | "requires": { 219 | "babel-runtime": "^6.22.0", 220 | "babel-types": "^6.24.1" 221 | } 222 | }, 223 | "babel-helper-hoist-variables": { 224 | "version": "6.24.1", 225 | "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", 226 | "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", 227 | "dev": true, 228 | "requires": { 229 | "babel-runtime": "^6.22.0", 230 | "babel-types": "^6.24.1" 231 | } 232 | }, 233 | "babel-helper-optimise-call-expression": { 234 | "version": "6.24.1", 235 | "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", 236 | "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", 237 | "dev": true, 238 | "requires": { 239 | "babel-runtime": "^6.22.0", 240 | "babel-types": "^6.24.1" 241 | } 242 | }, 243 | "babel-helper-regex": { 244 | "version": "6.26.0", 245 | "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", 246 | "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", 247 | "dev": true, 248 | "requires": { 249 | "babel-runtime": "^6.26.0", 250 | "babel-types": "^6.26.0", 251 | "lodash": "^4.17.4" 252 | } 253 | }, 254 | "babel-helper-remap-async-to-generator": { 255 | "version": "6.24.1", 256 | "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", 257 | "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", 258 | "dev": true, 259 | "requires": { 260 | "babel-helper-function-name": "^6.24.1", 261 | "babel-runtime": "^6.22.0", 262 | "babel-template": "^6.24.1", 263 | "babel-traverse": "^6.24.1", 264 | "babel-types": "^6.24.1" 265 | } 266 | }, 267 | "babel-helper-replace-supers": { 268 | "version": "6.24.1", 269 | "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", 270 | "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", 271 | "dev": true, 272 | "requires": { 273 | "babel-helper-optimise-call-expression": "^6.24.1", 274 | "babel-messages": "^6.23.0", 275 | "babel-runtime": "^6.22.0", 276 | "babel-template": "^6.24.1", 277 | "babel-traverse": "^6.24.1", 278 | "babel-types": "^6.24.1" 279 | } 280 | }, 281 | "babel-helpers": { 282 | "version": "6.24.1", 283 | "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", 284 | "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", 285 | "dev": true, 286 | "requires": { 287 | "babel-runtime": "^6.22.0", 288 | "babel-template": "^6.24.1" 289 | } 290 | }, 291 | "babel-messages": { 292 | "version": "6.23.0", 293 | "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", 294 | "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", 295 | "dev": true, 296 | "requires": { 297 | "babel-runtime": "^6.22.0" 298 | } 299 | }, 300 | "babel-plugin-check-es2015-constants": { 301 | "version": "6.22.0", 302 | "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", 303 | "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", 304 | "dev": true, 305 | "requires": { 306 | "babel-runtime": "^6.22.0" 307 | } 308 | }, 309 | "babel-plugin-syntax-async-functions": { 310 | "version": "6.13.0", 311 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", 312 | "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", 313 | "dev": true 314 | }, 315 | "babel-plugin-syntax-exponentiation-operator": { 316 | "version": "6.13.0", 317 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", 318 | "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", 319 | "dev": true 320 | }, 321 | "babel-plugin-syntax-trailing-function-commas": { 322 | "version": "6.22.0", 323 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", 324 | "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", 325 | "dev": true 326 | }, 327 | "babel-plugin-transform-async-to-generator": { 328 | "version": "6.24.1", 329 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", 330 | "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", 331 | "dev": true, 332 | "requires": { 333 | "babel-helper-remap-async-to-generator": "^6.24.1", 334 | "babel-plugin-syntax-async-functions": "^6.8.0", 335 | "babel-runtime": "^6.22.0" 336 | } 337 | }, 338 | "babel-plugin-transform-es2015-arrow-functions": { 339 | "version": "6.22.0", 340 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", 341 | "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", 342 | "dev": true, 343 | "requires": { 344 | "babel-runtime": "^6.22.0" 345 | } 346 | }, 347 | "babel-plugin-transform-es2015-block-scoped-functions": { 348 | "version": "6.22.0", 349 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", 350 | "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", 351 | "dev": true, 352 | "requires": { 353 | "babel-runtime": "^6.22.0" 354 | } 355 | }, 356 | "babel-plugin-transform-es2015-block-scoping": { 357 | "version": "6.26.0", 358 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", 359 | "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", 360 | "dev": true, 361 | "requires": { 362 | "babel-runtime": "^6.26.0", 363 | "babel-template": "^6.26.0", 364 | "babel-traverse": "^6.26.0", 365 | "babel-types": "^6.26.0", 366 | "lodash": "^4.17.4" 367 | } 368 | }, 369 | "babel-plugin-transform-es2015-classes": { 370 | "version": "6.24.1", 371 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", 372 | "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", 373 | "dev": true, 374 | "requires": { 375 | "babel-helper-define-map": "^6.24.1", 376 | "babel-helper-function-name": "^6.24.1", 377 | "babel-helper-optimise-call-expression": "^6.24.1", 378 | "babel-helper-replace-supers": "^6.24.1", 379 | "babel-messages": "^6.23.0", 380 | "babel-runtime": "^6.22.0", 381 | "babel-template": "^6.24.1", 382 | "babel-traverse": "^6.24.1", 383 | "babel-types": "^6.24.1" 384 | } 385 | }, 386 | "babel-plugin-transform-es2015-computed-properties": { 387 | "version": "6.24.1", 388 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", 389 | "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", 390 | "dev": true, 391 | "requires": { 392 | "babel-runtime": "^6.22.0", 393 | "babel-template": "^6.24.1" 394 | } 395 | }, 396 | "babel-plugin-transform-es2015-destructuring": { 397 | "version": "6.23.0", 398 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", 399 | "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", 400 | "dev": true, 401 | "requires": { 402 | "babel-runtime": "^6.22.0" 403 | } 404 | }, 405 | "babel-plugin-transform-es2015-duplicate-keys": { 406 | "version": "6.24.1", 407 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", 408 | "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", 409 | "dev": true, 410 | "requires": { 411 | "babel-runtime": "^6.22.0", 412 | "babel-types": "^6.24.1" 413 | } 414 | }, 415 | "babel-plugin-transform-es2015-for-of": { 416 | "version": "6.23.0", 417 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", 418 | "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", 419 | "dev": true, 420 | "requires": { 421 | "babel-runtime": "^6.22.0" 422 | } 423 | }, 424 | "babel-plugin-transform-es2015-function-name": { 425 | "version": "6.24.1", 426 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", 427 | "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", 428 | "dev": true, 429 | "requires": { 430 | "babel-helper-function-name": "^6.24.1", 431 | "babel-runtime": "^6.22.0", 432 | "babel-types": "^6.24.1" 433 | } 434 | }, 435 | "babel-plugin-transform-es2015-literals": { 436 | "version": "6.22.0", 437 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", 438 | "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", 439 | "dev": true, 440 | "requires": { 441 | "babel-runtime": "^6.22.0" 442 | } 443 | }, 444 | "babel-plugin-transform-es2015-modules-amd": { 445 | "version": "6.24.1", 446 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", 447 | "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", 448 | "dev": true, 449 | "requires": { 450 | "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", 451 | "babel-runtime": "^6.22.0", 452 | "babel-template": "^6.24.1" 453 | } 454 | }, 455 | "babel-plugin-transform-es2015-modules-commonjs": { 456 | "version": "6.26.2", 457 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", 458 | "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", 459 | "dev": true, 460 | "requires": { 461 | "babel-plugin-transform-strict-mode": "^6.24.1", 462 | "babel-runtime": "^6.26.0", 463 | "babel-template": "^6.26.0", 464 | "babel-types": "^6.26.0" 465 | } 466 | }, 467 | "babel-plugin-transform-es2015-modules-systemjs": { 468 | "version": "6.24.1", 469 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", 470 | "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", 471 | "dev": true, 472 | "requires": { 473 | "babel-helper-hoist-variables": "^6.24.1", 474 | "babel-runtime": "^6.22.0", 475 | "babel-template": "^6.24.1" 476 | } 477 | }, 478 | "babel-plugin-transform-es2015-modules-umd": { 479 | "version": "6.24.1", 480 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", 481 | "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", 482 | "dev": true, 483 | "requires": { 484 | "babel-plugin-transform-es2015-modules-amd": "^6.24.1", 485 | "babel-runtime": "^6.22.0", 486 | "babel-template": "^6.24.1" 487 | } 488 | }, 489 | "babel-plugin-transform-es2015-object-super": { 490 | "version": "6.24.1", 491 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", 492 | "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", 493 | "dev": true, 494 | "requires": { 495 | "babel-helper-replace-supers": "^6.24.1", 496 | "babel-runtime": "^6.22.0" 497 | } 498 | }, 499 | "babel-plugin-transform-es2015-parameters": { 500 | "version": "6.24.1", 501 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", 502 | "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", 503 | "dev": true, 504 | "requires": { 505 | "babel-helper-call-delegate": "^6.24.1", 506 | "babel-helper-get-function-arity": "^6.24.1", 507 | "babel-runtime": "^6.22.0", 508 | "babel-template": "^6.24.1", 509 | "babel-traverse": "^6.24.1", 510 | "babel-types": "^6.24.1" 511 | } 512 | }, 513 | "babel-plugin-transform-es2015-shorthand-properties": { 514 | "version": "6.24.1", 515 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", 516 | "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", 517 | "dev": true, 518 | "requires": { 519 | "babel-runtime": "^6.22.0", 520 | "babel-types": "^6.24.1" 521 | } 522 | }, 523 | "babel-plugin-transform-es2015-spread": { 524 | "version": "6.22.0", 525 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", 526 | "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", 527 | "dev": true, 528 | "requires": { 529 | "babel-runtime": "^6.22.0" 530 | } 531 | }, 532 | "babel-plugin-transform-es2015-sticky-regex": { 533 | "version": "6.24.1", 534 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", 535 | "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", 536 | "dev": true, 537 | "requires": { 538 | "babel-helper-regex": "^6.24.1", 539 | "babel-runtime": "^6.22.0", 540 | "babel-types": "^6.24.1" 541 | } 542 | }, 543 | "babel-plugin-transform-es2015-template-literals": { 544 | "version": "6.22.0", 545 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", 546 | "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", 547 | "dev": true, 548 | "requires": { 549 | "babel-runtime": "^6.22.0" 550 | } 551 | }, 552 | "babel-plugin-transform-es2015-typeof-symbol": { 553 | "version": "6.23.0", 554 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", 555 | "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", 556 | "dev": true, 557 | "requires": { 558 | "babel-runtime": "^6.22.0" 559 | } 560 | }, 561 | "babel-plugin-transform-es2015-unicode-regex": { 562 | "version": "6.24.1", 563 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", 564 | "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", 565 | "dev": true, 566 | "requires": { 567 | "babel-helper-regex": "^6.24.1", 568 | "babel-runtime": "^6.22.0", 569 | "regexpu-core": "^2.0.0" 570 | } 571 | }, 572 | "babel-plugin-transform-exponentiation-operator": { 573 | "version": "6.24.1", 574 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", 575 | "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", 576 | "dev": true, 577 | "requires": { 578 | "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", 579 | "babel-plugin-syntax-exponentiation-operator": "^6.8.0", 580 | "babel-runtime": "^6.22.0" 581 | } 582 | }, 583 | "babel-plugin-transform-regenerator": { 584 | "version": "6.26.0", 585 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", 586 | "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", 587 | "dev": true, 588 | "requires": { 589 | "regenerator-transform": "^0.10.0" 590 | } 591 | }, 592 | "babel-plugin-transform-strict-mode": { 593 | "version": "6.24.1", 594 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", 595 | "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", 596 | "dev": true, 597 | "requires": { 598 | "babel-runtime": "^6.22.0", 599 | "babel-types": "^6.24.1" 600 | } 601 | }, 602 | "babel-preset-env": { 603 | "version": "1.7.0", 604 | "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", 605 | "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", 606 | "dev": true, 607 | "requires": { 608 | "babel-plugin-check-es2015-constants": "^6.22.0", 609 | "babel-plugin-syntax-trailing-function-commas": "^6.22.0", 610 | "babel-plugin-transform-async-to-generator": "^6.22.0", 611 | "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", 612 | "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", 613 | "babel-plugin-transform-es2015-block-scoping": "^6.23.0", 614 | "babel-plugin-transform-es2015-classes": "^6.23.0", 615 | "babel-plugin-transform-es2015-computed-properties": "^6.22.0", 616 | "babel-plugin-transform-es2015-destructuring": "^6.23.0", 617 | "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", 618 | "babel-plugin-transform-es2015-for-of": "^6.23.0", 619 | "babel-plugin-transform-es2015-function-name": "^6.22.0", 620 | "babel-plugin-transform-es2015-literals": "^6.22.0", 621 | "babel-plugin-transform-es2015-modules-amd": "^6.22.0", 622 | "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", 623 | "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", 624 | "babel-plugin-transform-es2015-modules-umd": "^6.23.0", 625 | "babel-plugin-transform-es2015-object-super": "^6.22.0", 626 | "babel-plugin-transform-es2015-parameters": "^6.23.0", 627 | "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", 628 | "babel-plugin-transform-es2015-spread": "^6.22.0", 629 | "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", 630 | "babel-plugin-transform-es2015-template-literals": "^6.22.0", 631 | "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", 632 | "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", 633 | "babel-plugin-transform-exponentiation-operator": "^6.22.0", 634 | "babel-plugin-transform-regenerator": "^6.22.0", 635 | "browserslist": "^3.2.6", 636 | "invariant": "^2.2.2", 637 | "semver": "^5.3.0" 638 | } 639 | }, 640 | "babel-register": { 641 | "version": "6.26.0", 642 | "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", 643 | "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", 644 | "dev": true, 645 | "requires": { 646 | "babel-core": "^6.26.0", 647 | "babel-runtime": "^6.26.0", 648 | "core-js": "^2.5.0", 649 | "home-or-tmp": "^2.0.0", 650 | "lodash": "^4.17.4", 651 | "mkdirp": "^0.5.1", 652 | "source-map-support": "^0.4.15" 653 | } 654 | }, 655 | "babel-runtime": { 656 | "version": "6.26.0", 657 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 658 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 659 | "dev": true, 660 | "requires": { 661 | "core-js": "^2.4.0", 662 | "regenerator-runtime": "^0.11.0" 663 | } 664 | }, 665 | "babel-template": { 666 | "version": "6.26.0", 667 | "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", 668 | "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", 669 | "dev": true, 670 | "requires": { 671 | "babel-runtime": "^6.26.0", 672 | "babel-traverse": "^6.26.0", 673 | "babel-types": "^6.26.0", 674 | "babylon": "^6.18.0", 675 | "lodash": "^4.17.4" 676 | } 677 | }, 678 | "babel-traverse": { 679 | "version": "6.26.0", 680 | "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", 681 | "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", 682 | "dev": true, 683 | "requires": { 684 | "babel-code-frame": "^6.26.0", 685 | "babel-messages": "^6.23.0", 686 | "babel-runtime": "^6.26.0", 687 | "babel-types": "^6.26.0", 688 | "babylon": "^6.18.0", 689 | "debug": "^2.6.8", 690 | "globals": "^9.18.0", 691 | "invariant": "^2.2.2", 692 | "lodash": "^4.17.4" 693 | } 694 | }, 695 | "babel-types": { 696 | "version": "6.26.0", 697 | "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", 698 | "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", 699 | "dev": true, 700 | "requires": { 701 | "babel-runtime": "^6.26.0", 702 | "esutils": "^2.0.2", 703 | "lodash": "^4.17.4", 704 | "to-fast-properties": "^1.0.3" 705 | } 706 | }, 707 | "babylon": { 708 | "version": "6.18.0", 709 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 710 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", 711 | "dev": true 712 | }, 713 | "balanced-match": { 714 | "version": "1.0.0", 715 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 716 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 717 | }, 718 | "bluebird": { 719 | "version": "3.7.1", 720 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", 721 | "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" 722 | }, 723 | "body": { 724 | "version": "5.1.0", 725 | "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", 726 | "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", 727 | "dev": true, 728 | "requires": { 729 | "continuable-cache": "^0.3.1", 730 | "error": "^7.0.0", 731 | "raw-body": "~1.1.0", 732 | "safe-json-parse": "~1.0.1" 733 | } 734 | }, 735 | "brace-expansion": { 736 | "version": "1.1.8", 737 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 738 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 739 | "requires": { 740 | "balanced-match": "^1.0.0", 741 | "concat-map": "0.0.1" 742 | } 743 | }, 744 | "browserslist": { 745 | "version": "3.2.8", 746 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", 747 | "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", 748 | "dev": true, 749 | "requires": { 750 | "caniuse-lite": "^1.0.30000844", 751 | "electron-to-chromium": "^1.3.47" 752 | } 753 | }, 754 | "bytes": { 755 | "version": "1.0.0", 756 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", 757 | "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", 758 | "dev": true 759 | }, 760 | "camelcase": { 761 | "version": "2.1.1", 762 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 763 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", 764 | "dev": true 765 | }, 766 | "camelcase-keys": { 767 | "version": "2.1.0", 768 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", 769 | "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", 770 | "dev": true, 771 | "requires": { 772 | "camelcase": "^2.0.0", 773 | "map-obj": "^1.0.0" 774 | } 775 | }, 776 | "caniuse-lite": { 777 | "version": "1.0.30001005", 778 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001005.tgz", 779 | "integrity": "sha512-g78miZm1Z5njjYR216a5812oPiLgV1ssndgGxITHWUopmjUrCswMisA0a2kSB7a0vZRox6JOKhM51+efmYN8Mg==", 780 | "dev": true 781 | }, 782 | "chalk": { 783 | "version": "1.1.3", 784 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 785 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 786 | "dev": true, 787 | "requires": { 788 | "ansi-styles": "^2.2.1", 789 | "escape-string-regexp": "^1.0.2", 790 | "has-ansi": "^2.0.0", 791 | "strip-ansi": "^3.0.0", 792 | "supports-color": "^2.0.0" 793 | } 794 | }, 795 | "charm": { 796 | "version": "1.0.2", 797 | "resolved": "https://registry.npmjs.org/charm/-/charm-1.0.2.tgz", 798 | "integrity": "sha1-it02cVOm2aWBMxBSxAkJkdqZXjU=", 799 | "requires": { 800 | "inherits": "^2.0.1" 801 | } 802 | }, 803 | "coffeescript": { 804 | "version": "1.10.0", 805 | "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", 806 | "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=", 807 | "dev": true 808 | }, 809 | "color-convert": { 810 | "version": "1.9.3", 811 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 812 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 813 | "dev": true, 814 | "requires": { 815 | "color-name": "1.1.3" 816 | } 817 | }, 818 | "color-name": { 819 | "version": "1.1.3", 820 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 821 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 822 | "dev": true 823 | }, 824 | "colors": { 825 | "version": "1.1.2", 826 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", 827 | "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", 828 | "dev": true 829 | }, 830 | "concat-map": { 831 | "version": "0.0.1", 832 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 833 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 834 | }, 835 | "continuable-cache": { 836 | "version": "0.3.1", 837 | "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", 838 | "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", 839 | "dev": true 840 | }, 841 | "convert-source-map": { 842 | "version": "1.5.1", 843 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", 844 | "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", 845 | "dev": true 846 | }, 847 | "core-js": { 848 | "version": "2.5.1", 849 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", 850 | "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", 851 | "dev": true 852 | }, 853 | "cross-spawn-async": { 854 | "version": "2.2.5", 855 | "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", 856 | "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", 857 | "requires": { 858 | "lru-cache": "^4.0.0", 859 | "which": "^1.2.8" 860 | }, 861 | "dependencies": { 862 | "lru-cache": { 863 | "version": "4.1.1", 864 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", 865 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", 866 | "requires": { 867 | "pseudomap": "^1.0.2", 868 | "yallist": "^2.1.2" 869 | } 870 | }, 871 | "which": { 872 | "version": "1.3.0", 873 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 874 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 875 | "requires": { 876 | "isexe": "^2.0.0" 877 | } 878 | } 879 | } 880 | }, 881 | "cson-parser": { 882 | "version": "1.3.5", 883 | "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", 884 | "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", 885 | "dev": true, 886 | "requires": { 887 | "coffee-script": "^1.10.0" 888 | }, 889 | "dependencies": { 890 | "coffee-script": { 891 | "version": "1.12.7", 892 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", 893 | "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", 894 | "dev": true 895 | } 896 | } 897 | }, 898 | "currently-unhandled": { 899 | "version": "0.4.1", 900 | "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", 901 | "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", 902 | "dev": true, 903 | "requires": { 904 | "array-find-index": "^1.0.1" 905 | } 906 | }, 907 | "dateformat": { 908 | "version": "1.0.12", 909 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", 910 | "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", 911 | "dev": true, 912 | "requires": { 913 | "get-stdin": "^4.0.1", 914 | "meow": "^3.3.0" 915 | } 916 | }, 917 | "debug": { 918 | "version": "2.6.9", 919 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 920 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 921 | "dev": true, 922 | "requires": { 923 | "ms": "2.0.0" 924 | } 925 | }, 926 | "decamelize": { 927 | "version": "1.2.0", 928 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 929 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 930 | "dev": true 931 | }, 932 | "detect-indent": { 933 | "version": "4.0.0", 934 | "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", 935 | "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", 936 | "dev": true, 937 | "requires": { 938 | "repeating": "^2.0.0" 939 | } 940 | }, 941 | "electron-to-chromium": { 942 | "version": "1.3.296", 943 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz", 944 | "integrity": "sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ==", 945 | "dev": true 946 | }, 947 | "error": { 948 | "version": "7.0.2", 949 | "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", 950 | "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", 951 | "dev": true, 952 | "requires": { 953 | "string-template": "~0.2.1", 954 | "xtend": "~4.0.0" 955 | } 956 | }, 957 | "error-ex": { 958 | "version": "1.3.2", 959 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 960 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 961 | "dev": true, 962 | "requires": { 963 | "is-arrayish": "^0.2.1" 964 | } 965 | }, 966 | "escape-string-regexp": { 967 | "version": "1.0.5", 968 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 969 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 970 | "dev": true 971 | }, 972 | "esprima": { 973 | "version": "4.0.1", 974 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 975 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 976 | "dev": true 977 | }, 978 | "esutils": { 979 | "version": "2.0.2", 980 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 981 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 982 | "dev": true 983 | }, 984 | "eventemitter2": { 985 | "version": "0.4.14", 986 | "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", 987 | "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", 988 | "dev": true 989 | }, 990 | "exit": { 991 | "version": "0.1.2", 992 | "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 993 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 994 | "dev": true 995 | }, 996 | "faye-websocket": { 997 | "version": "0.10.0", 998 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", 999 | "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", 1000 | "dev": true, 1001 | "requires": { 1002 | "websocket-driver": ">=0.5.1" 1003 | } 1004 | }, 1005 | "file-sync-cmp": { 1006 | "version": "0.1.1", 1007 | "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", 1008 | "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", 1009 | "dev": true 1010 | }, 1011 | "find-up": { 1012 | "version": "1.1.2", 1013 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 1014 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 1015 | "dev": true, 1016 | "requires": { 1017 | "path-exists": "^2.0.0", 1018 | "pinkie-promise": "^2.0.0" 1019 | } 1020 | }, 1021 | "findup-sync": { 1022 | "version": "0.3.0", 1023 | "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", 1024 | "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", 1025 | "dev": true, 1026 | "requires": { 1027 | "glob": "~5.0.0" 1028 | }, 1029 | "dependencies": { 1030 | "glob": { 1031 | "version": "5.0.15", 1032 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 1033 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 1034 | "dev": true, 1035 | "requires": { 1036 | "inflight": "^1.0.4", 1037 | "inherits": "2", 1038 | "minimatch": "2 || 3", 1039 | "once": "^1.3.0", 1040 | "path-is-absolute": "^1.0.0" 1041 | } 1042 | } 1043 | } 1044 | }, 1045 | "fs.realpath": { 1046 | "version": "1.0.0", 1047 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1048 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 1049 | }, 1050 | "gaze": { 1051 | "version": "1.1.3", 1052 | "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", 1053 | "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", 1054 | "dev": true, 1055 | "requires": { 1056 | "globule": "^1.0.0" 1057 | } 1058 | }, 1059 | "get-stdin": { 1060 | "version": "4.0.1", 1061 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 1062 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", 1063 | "dev": true 1064 | }, 1065 | "getobject": { 1066 | "version": "0.1.0", 1067 | "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", 1068 | "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", 1069 | "dev": true 1070 | }, 1071 | "glob": { 1072 | "version": "7.1.5", 1073 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", 1074 | "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", 1075 | "requires": { 1076 | "fs.realpath": "^1.0.0", 1077 | "inflight": "^1.0.4", 1078 | "inherits": "2", 1079 | "minimatch": "^3.0.4", 1080 | "once": "^1.3.0", 1081 | "path-is-absolute": "^1.0.0" 1082 | } 1083 | }, 1084 | "globals": { 1085 | "version": "9.18.0", 1086 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 1087 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", 1088 | "dev": true 1089 | }, 1090 | "globule": { 1091 | "version": "1.2.1", 1092 | "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", 1093 | "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", 1094 | "dev": true, 1095 | "requires": { 1096 | "glob": "~7.1.1", 1097 | "lodash": "~4.17.10", 1098 | "minimatch": "~3.0.2" 1099 | } 1100 | }, 1101 | "graceful-fs": { 1102 | "version": "4.2.3", 1103 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 1104 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", 1105 | "dev": true 1106 | }, 1107 | "grunt": { 1108 | "version": "1.0.4", 1109 | "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz", 1110 | "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==", 1111 | "dev": true, 1112 | "requires": { 1113 | "coffeescript": "~1.10.0", 1114 | "dateformat": "~1.0.12", 1115 | "eventemitter2": "~0.4.13", 1116 | "exit": "~0.1.1", 1117 | "findup-sync": "~0.3.0", 1118 | "glob": "~7.0.0", 1119 | "grunt-cli": "~1.2.0", 1120 | "grunt-known-options": "~1.1.0", 1121 | "grunt-legacy-log": "~2.0.0", 1122 | "grunt-legacy-util": "~1.1.1", 1123 | "iconv-lite": "~0.4.13", 1124 | "js-yaml": "~3.13.0", 1125 | "minimatch": "~3.0.2", 1126 | "mkdirp": "~0.5.1", 1127 | "nopt": "~3.0.6", 1128 | "path-is-absolute": "~1.0.0", 1129 | "rimraf": "~2.6.2" 1130 | }, 1131 | "dependencies": { 1132 | "esprima": { 1133 | "version": "4.0.1", 1134 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 1135 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 1136 | "dev": true 1137 | }, 1138 | "glob": { 1139 | "version": "7.0.6", 1140 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", 1141 | "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", 1142 | "dev": true, 1143 | "requires": { 1144 | "fs.realpath": "^1.0.0", 1145 | "inflight": "^1.0.4", 1146 | "inherits": "2", 1147 | "minimatch": "^3.0.2", 1148 | "once": "^1.3.0", 1149 | "path-is-absolute": "^1.0.0" 1150 | } 1151 | }, 1152 | "grunt-cli": { 1153 | "version": "1.2.0", 1154 | "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", 1155 | "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", 1156 | "dev": true, 1157 | "requires": { 1158 | "findup-sync": "~0.3.0", 1159 | "grunt-known-options": "~1.1.0", 1160 | "nopt": "~3.0.6", 1161 | "resolve": "~1.1.0" 1162 | } 1163 | }, 1164 | "js-yaml": { 1165 | "version": "3.13.1", 1166 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1167 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1168 | "dev": true, 1169 | "requires": { 1170 | "argparse": "^1.0.7", 1171 | "esprima": "^4.0.0" 1172 | } 1173 | }, 1174 | "resolve": { 1175 | "version": "1.1.7", 1176 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", 1177 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 1178 | "dev": true 1179 | } 1180 | } 1181 | }, 1182 | "grunt-babel": { 1183 | "version": "6.0.0", 1184 | "resolved": "https://registry.npmjs.org/grunt-babel/-/grunt-babel-6.0.0.tgz", 1185 | "integrity": "sha1-N4GJtIfeEWjExKn8iN1gBbNd+WA=", 1186 | "dev": true, 1187 | "requires": { 1188 | "babel-core": "^6.0.12" 1189 | } 1190 | }, 1191 | "grunt-contrib-concat": { 1192 | "version": "1.0.1", 1193 | "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", 1194 | "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", 1195 | "dev": true, 1196 | "requires": { 1197 | "chalk": "^1.0.0", 1198 | "source-map": "^0.5.3" 1199 | } 1200 | }, 1201 | "grunt-contrib-watch": { 1202 | "version": "1.1.0", 1203 | "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", 1204 | "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", 1205 | "dev": true, 1206 | "requires": { 1207 | "async": "^2.6.0", 1208 | "gaze": "^1.1.0", 1209 | "lodash": "^4.17.10", 1210 | "tiny-lr": "^1.1.1" 1211 | }, 1212 | "dependencies": { 1213 | "async": { 1214 | "version": "2.6.2", 1215 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", 1216 | "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", 1217 | "dev": true, 1218 | "requires": { 1219 | "lodash": "^4.17.11" 1220 | } 1221 | }, 1222 | "lodash": { 1223 | "version": "4.17.15", 1224 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1225 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 1226 | "dev": true 1227 | } 1228 | } 1229 | }, 1230 | "grunt-known-options": { 1231 | "version": "1.1.1", 1232 | "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", 1233 | "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", 1234 | "dev": true 1235 | }, 1236 | "grunt-legacy-log": { 1237 | "version": "2.0.0", 1238 | "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz", 1239 | "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==", 1240 | "dev": true, 1241 | "requires": { 1242 | "colors": "~1.1.2", 1243 | "grunt-legacy-log-utils": "~2.0.0", 1244 | "hooker": "~0.2.3", 1245 | "lodash": "~4.17.5" 1246 | } 1247 | }, 1248 | "grunt-legacy-log-utils": { 1249 | "version": "2.0.1", 1250 | "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz", 1251 | "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==", 1252 | "dev": true, 1253 | "requires": { 1254 | "chalk": "~2.4.1", 1255 | "lodash": "~4.17.10" 1256 | }, 1257 | "dependencies": { 1258 | "ansi-styles": { 1259 | "version": "3.2.1", 1260 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1261 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1262 | "dev": true, 1263 | "requires": { 1264 | "color-convert": "^1.9.0" 1265 | } 1266 | }, 1267 | "chalk": { 1268 | "version": "2.4.2", 1269 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1270 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1271 | "dev": true, 1272 | "requires": { 1273 | "ansi-styles": "^3.2.1", 1274 | "escape-string-regexp": "^1.0.5", 1275 | "supports-color": "^5.3.0" 1276 | } 1277 | }, 1278 | "supports-color": { 1279 | "version": "5.5.0", 1280 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1281 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1282 | "dev": true, 1283 | "requires": { 1284 | "has-flag": "^3.0.0" 1285 | } 1286 | } 1287 | } 1288 | }, 1289 | "grunt-legacy-util": { 1290 | "version": "1.1.1", 1291 | "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz", 1292 | "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==", 1293 | "dev": true, 1294 | "requires": { 1295 | "async": "~1.5.2", 1296 | "exit": "~0.1.1", 1297 | "getobject": "~0.1.0", 1298 | "hooker": "~0.2.3", 1299 | "lodash": "~4.17.10", 1300 | "underscore.string": "~3.3.4", 1301 | "which": "~1.3.0" 1302 | } 1303 | }, 1304 | "grunt-replace": { 1305 | "version": "1.0.1", 1306 | "resolved": "https://registry.npmjs.org/grunt-replace/-/grunt-replace-1.0.1.tgz", 1307 | "integrity": "sha1-kKeVMvuJBB/kJ8h9QlI4sPiGZRo=", 1308 | "dev": true, 1309 | "requires": { 1310 | "applause": "1.2.2", 1311 | "chalk": "^1.1.0", 1312 | "file-sync-cmp": "^0.1.0", 1313 | "lodash": "^4.11.0" 1314 | } 1315 | }, 1316 | "has-ansi": { 1317 | "version": "2.0.0", 1318 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 1319 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 1320 | "dev": true, 1321 | "requires": { 1322 | "ansi-regex": "^2.0.0" 1323 | } 1324 | }, 1325 | "has-flag": { 1326 | "version": "3.0.0", 1327 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1328 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1329 | "dev": true 1330 | }, 1331 | "home-or-tmp": { 1332 | "version": "2.0.0", 1333 | "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", 1334 | "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", 1335 | "dev": true, 1336 | "requires": { 1337 | "os-homedir": "^1.0.0", 1338 | "os-tmpdir": "^1.0.1" 1339 | } 1340 | }, 1341 | "hooker": { 1342 | "version": "0.2.3", 1343 | "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", 1344 | "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", 1345 | "dev": true 1346 | }, 1347 | "hosted-git-info": { 1348 | "version": "2.8.5", 1349 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", 1350 | "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", 1351 | "dev": true 1352 | }, 1353 | "http-parser-js": { 1354 | "version": "0.5.0", 1355 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", 1356 | "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", 1357 | "dev": true 1358 | }, 1359 | "iconv-lite": { 1360 | "version": "0.4.24", 1361 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1362 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1363 | "dev": true, 1364 | "requires": { 1365 | "safer-buffer": ">= 2.1.2 < 3" 1366 | } 1367 | }, 1368 | "indent-string": { 1369 | "version": "2.1.0", 1370 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 1371 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 1372 | "dev": true, 1373 | "requires": { 1374 | "repeating": "^2.0.0" 1375 | } 1376 | }, 1377 | "inflight": { 1378 | "version": "1.0.6", 1379 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1380 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1381 | "requires": { 1382 | "once": "^1.3.0", 1383 | "wrappy": "1" 1384 | } 1385 | }, 1386 | "inherits": { 1387 | "version": "2.0.3", 1388 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1389 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1390 | }, 1391 | "invariant": { 1392 | "version": "2.2.2", 1393 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", 1394 | "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", 1395 | "dev": true, 1396 | "requires": { 1397 | "loose-envify": "^1.0.0" 1398 | } 1399 | }, 1400 | "is-arrayish": { 1401 | "version": "0.2.1", 1402 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 1403 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 1404 | "dev": true 1405 | }, 1406 | "is-finite": { 1407 | "version": "1.0.2", 1408 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 1409 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 1410 | "dev": true, 1411 | "requires": { 1412 | "number-is-nan": "^1.0.0" 1413 | } 1414 | }, 1415 | "is-utf8": { 1416 | "version": "0.2.1", 1417 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 1418 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", 1419 | "dev": true 1420 | }, 1421 | "isexe": { 1422 | "version": "2.0.0", 1423 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1424 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 1425 | }, 1426 | "js-tokens": { 1427 | "version": "3.0.2", 1428 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 1429 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 1430 | "dev": true 1431 | }, 1432 | "js-yaml": { 1433 | "version": "3.13.1", 1434 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1435 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1436 | "dev": true, 1437 | "requires": { 1438 | "argparse": "^1.0.7", 1439 | "esprima": "^4.0.0" 1440 | } 1441 | }, 1442 | "jsesc": { 1443 | "version": "0.5.0", 1444 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", 1445 | "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", 1446 | "dev": true 1447 | }, 1448 | "json5": { 1449 | "version": "0.5.1", 1450 | "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", 1451 | "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", 1452 | "dev": true 1453 | }, 1454 | "livereload-js": { 1455 | "version": "2.4.0", 1456 | "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", 1457 | "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", 1458 | "dev": true 1459 | }, 1460 | "load-grunt-tasks": { 1461 | "version": "3.5.2", 1462 | "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz", 1463 | "integrity": "sha1-ByhWEYD9IP+KaSdQWFL8WKrqDIg=", 1464 | "dev": true, 1465 | "requires": { 1466 | "arrify": "^1.0.0", 1467 | "multimatch": "^2.0.0", 1468 | "pkg-up": "^1.0.0", 1469 | "resolve-pkg": "^0.1.0" 1470 | } 1471 | }, 1472 | "load-json-file": { 1473 | "version": "1.1.0", 1474 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 1475 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 1476 | "dev": true, 1477 | "requires": { 1478 | "graceful-fs": "^4.1.2", 1479 | "parse-json": "^2.2.0", 1480 | "pify": "^2.0.0", 1481 | "pinkie-promise": "^2.0.0", 1482 | "strip-bom": "^2.0.0" 1483 | } 1484 | }, 1485 | "lodash": { 1486 | "version": "4.17.15", 1487 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1488 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 1489 | "dev": true 1490 | }, 1491 | "loose-envify": { 1492 | "version": "1.3.1", 1493 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", 1494 | "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", 1495 | "dev": true, 1496 | "requires": { 1497 | "js-tokens": "^3.0.0" 1498 | } 1499 | }, 1500 | "loud-rejection": { 1501 | "version": "1.6.0", 1502 | "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", 1503 | "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", 1504 | "dev": true, 1505 | "requires": { 1506 | "currently-unhandled": "^0.4.1", 1507 | "signal-exit": "^3.0.0" 1508 | } 1509 | }, 1510 | "map-obj": { 1511 | "version": "1.0.1", 1512 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", 1513 | "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", 1514 | "dev": true 1515 | }, 1516 | "meow": { 1517 | "version": "3.7.0", 1518 | "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", 1519 | "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", 1520 | "dev": true, 1521 | "requires": { 1522 | "camelcase-keys": "^2.0.0", 1523 | "decamelize": "^1.1.2", 1524 | "loud-rejection": "^1.0.0", 1525 | "map-obj": "^1.0.1", 1526 | "minimist": "^1.1.3", 1527 | "normalize-package-data": "^2.3.4", 1528 | "object-assign": "^4.0.1", 1529 | "read-pkg-up": "^1.0.1", 1530 | "redent": "^1.0.0", 1531 | "trim-newlines": "^1.0.0" 1532 | } 1533 | }, 1534 | "minimatch": { 1535 | "version": "3.0.4", 1536 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1537 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1538 | "requires": { 1539 | "brace-expansion": "^1.1.7" 1540 | } 1541 | }, 1542 | "minimist": { 1543 | "version": "1.2.0", 1544 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1545 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 1546 | }, 1547 | "mkdirp": { 1548 | "version": "0.5.1", 1549 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1550 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1551 | "requires": { 1552 | "minimist": "0.0.8" 1553 | }, 1554 | "dependencies": { 1555 | "minimist": { 1556 | "version": "0.0.8", 1557 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1558 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1559 | } 1560 | } 1561 | }, 1562 | "ms": { 1563 | "version": "2.0.0", 1564 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1565 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1566 | "dev": true 1567 | }, 1568 | "multimatch": { 1569 | "version": "2.1.0", 1570 | "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", 1571 | "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", 1572 | "dev": true, 1573 | "requires": { 1574 | "array-differ": "^1.0.0", 1575 | "array-union": "^1.0.1", 1576 | "arrify": "^1.0.0", 1577 | "minimatch": "^3.0.0" 1578 | } 1579 | }, 1580 | "nopt": { 1581 | "version": "3.0.6", 1582 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 1583 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 1584 | "dev": true, 1585 | "requires": { 1586 | "abbrev": "1" 1587 | } 1588 | }, 1589 | "normalize-package-data": { 1590 | "version": "2.5.0", 1591 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 1592 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 1593 | "dev": true, 1594 | "requires": { 1595 | "hosted-git-info": "^2.1.4", 1596 | "resolve": "^1.10.0", 1597 | "semver": "2 || 3 || 4 || 5", 1598 | "validate-npm-package-license": "^3.0.1" 1599 | } 1600 | }, 1601 | "number-is-nan": { 1602 | "version": "1.0.1", 1603 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1604 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 1605 | "dev": true 1606 | }, 1607 | "object-assign": { 1608 | "version": "4.1.1", 1609 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1610 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1611 | "dev": true 1612 | }, 1613 | "once": { 1614 | "version": "1.4.0", 1615 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1616 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1617 | "requires": { 1618 | "wrappy": "1" 1619 | } 1620 | }, 1621 | "os-homedir": { 1622 | "version": "1.0.2", 1623 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1624 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 1625 | "dev": true 1626 | }, 1627 | "os-tmpdir": { 1628 | "version": "1.0.2", 1629 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1630 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 1631 | "dev": true 1632 | }, 1633 | "parse-json": { 1634 | "version": "2.2.0", 1635 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 1636 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 1637 | "dev": true, 1638 | "requires": { 1639 | "error-ex": "^1.2.0" 1640 | } 1641 | }, 1642 | "path-exists": { 1643 | "version": "2.1.0", 1644 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 1645 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 1646 | "dev": true, 1647 | "requires": { 1648 | "pinkie-promise": "^2.0.0" 1649 | } 1650 | }, 1651 | "path-is-absolute": { 1652 | "version": "1.0.1", 1653 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1654 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1655 | }, 1656 | "path-parse": { 1657 | "version": "1.0.6", 1658 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1659 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1660 | "dev": true 1661 | }, 1662 | "path-type": { 1663 | "version": "1.1.0", 1664 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 1665 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 1666 | "dev": true, 1667 | "requires": { 1668 | "graceful-fs": "^4.1.2", 1669 | "pify": "^2.0.0", 1670 | "pinkie-promise": "^2.0.0" 1671 | } 1672 | }, 1673 | "pify": { 1674 | "version": "2.3.0", 1675 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1676 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 1677 | "dev": true 1678 | }, 1679 | "pinkie": { 1680 | "version": "2.0.4", 1681 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1682 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1683 | "dev": true 1684 | }, 1685 | "pinkie-promise": { 1686 | "version": "2.0.1", 1687 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1688 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1689 | "dev": true, 1690 | "requires": { 1691 | "pinkie": "^2.0.0" 1692 | } 1693 | }, 1694 | "pkg-up": { 1695 | "version": "1.0.0", 1696 | "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", 1697 | "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", 1698 | "dev": true, 1699 | "requires": { 1700 | "find-up": "^1.0.0" 1701 | } 1702 | }, 1703 | "private": { 1704 | "version": "0.1.8", 1705 | "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", 1706 | "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", 1707 | "dev": true 1708 | }, 1709 | "pseudomap": { 1710 | "version": "1.0.2", 1711 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1712 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 1713 | }, 1714 | "qs": { 1715 | "version": "6.6.0", 1716 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.6.0.tgz", 1717 | "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==", 1718 | "dev": true 1719 | }, 1720 | "raw-body": { 1721 | "version": "1.1.7", 1722 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", 1723 | "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", 1724 | "dev": true, 1725 | "requires": { 1726 | "bytes": "1", 1727 | "string_decoder": "0.10" 1728 | } 1729 | }, 1730 | "read-pkg": { 1731 | "version": "1.1.0", 1732 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 1733 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 1734 | "dev": true, 1735 | "requires": { 1736 | "load-json-file": "^1.0.0", 1737 | "normalize-package-data": "^2.3.2", 1738 | "path-type": "^1.0.0" 1739 | } 1740 | }, 1741 | "read-pkg-up": { 1742 | "version": "1.0.1", 1743 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 1744 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 1745 | "dev": true, 1746 | "requires": { 1747 | "find-up": "^1.0.0", 1748 | "read-pkg": "^1.0.0" 1749 | } 1750 | }, 1751 | "redent": { 1752 | "version": "1.0.0", 1753 | "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", 1754 | "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", 1755 | "dev": true, 1756 | "requires": { 1757 | "indent-string": "^2.1.0", 1758 | "strip-indent": "^1.0.1" 1759 | } 1760 | }, 1761 | "regenerate": { 1762 | "version": "1.4.0", 1763 | "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", 1764 | "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", 1765 | "dev": true 1766 | }, 1767 | "regenerator-runtime": { 1768 | "version": "0.11.0", 1769 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", 1770 | "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", 1771 | "dev": true 1772 | }, 1773 | "regenerator-transform": { 1774 | "version": "0.10.1", 1775 | "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", 1776 | "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", 1777 | "dev": true, 1778 | "requires": { 1779 | "babel-runtime": "^6.18.0", 1780 | "babel-types": "^6.19.0", 1781 | "private": "^0.1.6" 1782 | } 1783 | }, 1784 | "regexpu-core": { 1785 | "version": "2.0.0", 1786 | "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", 1787 | "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", 1788 | "dev": true, 1789 | "requires": { 1790 | "regenerate": "^1.2.1", 1791 | "regjsgen": "^0.2.0", 1792 | "regjsparser": "^0.1.4" 1793 | } 1794 | }, 1795 | "regjsgen": { 1796 | "version": "0.2.0", 1797 | "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", 1798 | "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", 1799 | "dev": true 1800 | }, 1801 | "regjsparser": { 1802 | "version": "0.1.5", 1803 | "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", 1804 | "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", 1805 | "dev": true, 1806 | "requires": { 1807 | "jsesc": "~0.5.0" 1808 | } 1809 | }, 1810 | "repeating": { 1811 | "version": "2.0.1", 1812 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 1813 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 1814 | "dev": true, 1815 | "requires": { 1816 | "is-finite": "^1.0.0" 1817 | } 1818 | }, 1819 | "resolve": { 1820 | "version": "1.12.0", 1821 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", 1822 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", 1823 | "dev": true, 1824 | "requires": { 1825 | "path-parse": "^1.0.6" 1826 | } 1827 | }, 1828 | "resolve-from": { 1829 | "version": "2.0.0", 1830 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 1831 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", 1832 | "dev": true 1833 | }, 1834 | "resolve-pkg": { 1835 | "version": "0.1.0", 1836 | "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-0.1.0.tgz", 1837 | "integrity": "sha1-AsyZNBDik2livZcWahsHfalyVTE=", 1838 | "dev": true, 1839 | "requires": { 1840 | "resolve-from": "^2.0.0" 1841 | } 1842 | }, 1843 | "rimraf": { 1844 | "version": "2.6.3", 1845 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 1846 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 1847 | "dev": true, 1848 | "requires": { 1849 | "glob": "^7.1.3" 1850 | } 1851 | }, 1852 | "safe-json-parse": { 1853 | "version": "1.0.1", 1854 | "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", 1855 | "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", 1856 | "dev": true 1857 | }, 1858 | "safer-buffer": { 1859 | "version": "2.1.2", 1860 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1861 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1862 | "dev": true 1863 | }, 1864 | "semver": { 1865 | "version": "5.4.1", 1866 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 1867 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", 1868 | "dev": true 1869 | }, 1870 | "shell-quote": { 1871 | "version": "1.7.2", 1872 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", 1873 | "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" 1874 | }, 1875 | "signal-exit": { 1876 | "version": "3.0.2", 1877 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1878 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1879 | "dev": true 1880 | }, 1881 | "slash": { 1882 | "version": "1.0.0", 1883 | "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", 1884 | "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", 1885 | "dev": true 1886 | }, 1887 | "source-map": { 1888 | "version": "0.5.7", 1889 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1890 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1891 | "dev": true 1892 | }, 1893 | "source-map-support": { 1894 | "version": "0.4.18", 1895 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", 1896 | "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", 1897 | "dev": true, 1898 | "requires": { 1899 | "source-map": "^0.5.6" 1900 | } 1901 | }, 1902 | "spdx-correct": { 1903 | "version": "3.1.0", 1904 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 1905 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 1906 | "dev": true, 1907 | "requires": { 1908 | "spdx-expression-parse": "^3.0.0", 1909 | "spdx-license-ids": "^3.0.0" 1910 | } 1911 | }, 1912 | "spdx-exceptions": { 1913 | "version": "2.2.0", 1914 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1915 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 1916 | "dev": true 1917 | }, 1918 | "spdx-expression-parse": { 1919 | "version": "3.0.0", 1920 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1921 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1922 | "dev": true, 1923 | "requires": { 1924 | "spdx-exceptions": "^2.1.0", 1925 | "spdx-license-ids": "^3.0.0" 1926 | } 1927 | }, 1928 | "spdx-license-ids": { 1929 | "version": "3.0.5", 1930 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 1931 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 1932 | "dev": true 1933 | }, 1934 | "sprintf-js": { 1935 | "version": "1.0.3", 1936 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1937 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1938 | "dev": true 1939 | }, 1940 | "string-template": { 1941 | "version": "0.2.1", 1942 | "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", 1943 | "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", 1944 | "dev": true 1945 | }, 1946 | "string_decoder": { 1947 | "version": "0.10.31", 1948 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1949 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1950 | "dev": true 1951 | }, 1952 | "strip-ansi": { 1953 | "version": "3.0.1", 1954 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1955 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1956 | "dev": true, 1957 | "requires": { 1958 | "ansi-regex": "^2.0.0" 1959 | } 1960 | }, 1961 | "strip-bom": { 1962 | "version": "2.0.0", 1963 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 1964 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 1965 | "dev": true, 1966 | "requires": { 1967 | "is-utf8": "^0.2.0" 1968 | } 1969 | }, 1970 | "strip-indent": { 1971 | "version": "1.0.1", 1972 | "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 1973 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 1974 | "dev": true, 1975 | "requires": { 1976 | "get-stdin": "^4.0.1" 1977 | } 1978 | }, 1979 | "supports-color": { 1980 | "version": "2.0.0", 1981 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1982 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1983 | "dev": true 1984 | }, 1985 | "tiny-lr": { 1986 | "version": "1.1.1", 1987 | "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", 1988 | "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", 1989 | "dev": true, 1990 | "requires": { 1991 | "body": "^5.1.0", 1992 | "debug": "^3.1.0", 1993 | "faye-websocket": "~0.10.0", 1994 | "livereload-js": "^2.3.0", 1995 | "object-assign": "^4.1.0", 1996 | "qs": "^6.4.0" 1997 | }, 1998 | "dependencies": { 1999 | "debug": { 2000 | "version": "3.2.6", 2001 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 2002 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 2003 | "dev": true, 2004 | "requires": { 2005 | "ms": "^2.1.1" 2006 | } 2007 | }, 2008 | "ms": { 2009 | "version": "2.1.1", 2010 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 2011 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 2012 | "dev": true 2013 | } 2014 | } 2015 | }, 2016 | "to-fast-properties": { 2017 | "version": "1.0.3", 2018 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", 2019 | "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", 2020 | "dev": true 2021 | }, 2022 | "trim-newlines": { 2023 | "version": "1.0.0", 2024 | "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", 2025 | "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", 2026 | "dev": true 2027 | }, 2028 | "trim-right": { 2029 | "version": "1.0.1", 2030 | "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", 2031 | "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", 2032 | "dev": true 2033 | }, 2034 | "underscore.string": { 2035 | "version": "3.3.5", 2036 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", 2037 | "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", 2038 | "dev": true, 2039 | "requires": { 2040 | "sprintf-js": "^1.0.3", 2041 | "util-deprecate": "^1.0.2" 2042 | } 2043 | }, 2044 | "util-deprecate": { 2045 | "version": "1.0.2", 2046 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2047 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2048 | "dev": true 2049 | }, 2050 | "validate-npm-package-license": { 2051 | "version": "3.0.4", 2052 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 2053 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 2054 | "dev": true, 2055 | "requires": { 2056 | "spdx-correct": "^3.0.0", 2057 | "spdx-expression-parse": "^3.0.0" 2058 | } 2059 | }, 2060 | "websocket-driver": { 2061 | "version": "0.7.0", 2062 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", 2063 | "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", 2064 | "dev": true, 2065 | "requires": { 2066 | "http-parser-js": ">=0.4.0", 2067 | "websocket-extensions": ">=0.1.1" 2068 | } 2069 | }, 2070 | "websocket-extensions": { 2071 | "version": "0.1.3", 2072 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", 2073 | "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", 2074 | "dev": true 2075 | }, 2076 | "which": { 2077 | "version": "1.3.1", 2078 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2079 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2080 | "dev": true, 2081 | "requires": { 2082 | "isexe": "^2.0.0" 2083 | } 2084 | }, 2085 | "wrappy": { 2086 | "version": "1.0.2", 2087 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2088 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 2089 | }, 2090 | "xtend": { 2091 | "version": "4.0.1", 2092 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 2093 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", 2094 | "dev": true 2095 | }, 2096 | "yallist": { 2097 | "version": "2.1.2", 2098 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 2099 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 2100 | } 2101 | } 2102 | } 2103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batch-transcode-video", 3 | "version": "2.0.0", 4 | "description": "Batch transcode videos using the video_transcoding gem.", 5 | "contributors": [ 6 | "Nick Wronski (https://github.com/nwronski)" 7 | ], 8 | "keywords": [ 9 | "mkv", 10 | "transcode", 11 | "transcode-video" 12 | ], 13 | "engine": "node >= 4.0.0", 14 | "preferGlobal": true, 15 | "bin": { 16 | "batch-transcode-video": "bin/batch-transcode-video" 17 | }, 18 | "files": [ 19 | "bin/", 20 | "LICENSE", 21 | "CHANGELOG.md", 22 | "README.md", 23 | "dist/" 24 | ], 25 | "scripts": { 26 | "build": "grunt" 27 | }, 28 | "main": "dist/index.js", 29 | "author": "Nick Wronski (https://github.com/nwronski)", 30 | "repository": "git@github.com:nwronski/batch-transcode-video.git", 31 | "bugs": { 32 | "url": "https://github.com/nwronski/batch-transcode-video/issues" 33 | }, 34 | "license": "MIT", 35 | "dependencies": { 36 | "bluebird": "^3.7.1", 37 | "charm": "^1.0.2", 38 | "cross-spawn-async": "^2.2.5", 39 | "glob": "^7.1.5", 40 | "minimist": "^1.2.0", 41 | "mkdirp": "^0.5.1", 42 | "shell-quote": "^1.7.2" 43 | }, 44 | "devDependencies": { 45 | "babel-preset-env": "^1.7.0", 46 | "grunt": "^1.0.4", 47 | "grunt-babel": "^6.0.0", 48 | "grunt-contrib-concat": "^1.0.1", 49 | "grunt-contrib-watch": "^1.1.0", 50 | "grunt-replace": "^1.0.1", 51 | "load-grunt-tasks": "^3.5.2" 52 | } 53 | } 54 | --------------------------------------------------------------------------------