├── lib ├── consoleLogger.js ├── aspresets.json ├── logger.js ├── classes │ ├── encoder.js │ └── video.js ├── languages.json └── helpers.js ├── settings.sample.json ├── .gitignore ├── LICENSE ├── package.json ├── test.js ├── README.md ├── h265ize └── yarn.lock /lib/consoleLogger.js: -------------------------------------------------------------------------------- 1 | var exports = module.exports = { 2 | debug: console.log, 3 | verbose: console.log, 4 | info: console.info, 5 | error: console.error, 6 | alert: console.log, 7 | warn: console.error 8 | }; -------------------------------------------------------------------------------- /lib/aspresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "anime-high": { 3 | "x265Options": "weightb", 4 | "preset": "medium", 5 | "bitdepth": 10 6 | }, 7 | "anime-medium": { 8 | "x265Options": "weightb", 9 | "preset": "medium", 10 | "bitdepth": 10, 11 | "videoFilters": "deband=r=32:1thr=0.01:2thr=0.01:3thr=0.01:4thr=0.01" 12 | }, 13 | "anime": { 14 | "x265Options": "weightb", 15 | "preset": "medium", 16 | "bitdepth": 10, 17 | "videoFilters": "deband=r=32:1thr=0.01:2thr=0.01:3thr=0.01:4thr=0.01" 18 | }, 19 | "testing-ssim": { 20 | "x265Options": "tune=ssim:ssim:psy-rd=0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /settings.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "HOW TO USE": "Copy this file to settings.json and modify the settings below.", 3 | "destination": "/tmp", 4 | "preset": "veryfast", 5 | "as-preset": "none", 6 | "native-language": "eng", 7 | "output-format": "mp4", 8 | "extra-options": "b-frames=4:subme=6", 9 | "qality": 26, 10 | "video-bitrate": 0, 11 | "preview-length": 30000, 12 | "accurate-timestamps": false, 13 | "he-audio": true, 14 | "force-he-audio": true, 15 | "downmix-he-audio": true, 16 | "override": false, 17 | "preview": false, 18 | "sample": true, 19 | "multi-pass": 0, 20 | "stats": true, 21 | "verbose": true, 22 | "bitdepth": 8, 23 | "screenshots": true, 24 | "normalize-level": 3, 25 | "scale": 720, 26 | "debug": false, 27 | "delete": false, 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/ 2 | h265/ 3 | log.txt 4 | h265ize.csv 5 | settings.json 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules 38 | jspm_packages 39 | 40 | # Optional npm cache directory 41 | .npm 42 | 43 | # Optional eslint cache 44 | .eslintcache 45 | 46 | # Optional REPL history 47 | .node_repl_history 48 | 49 | # Output of 'npm pack' 50 | *.tgz 51 | 52 | # Yarn Integrity file 53 | .yarn-integrity 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 Ayrton Sparling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "h265ize", 3 | "version": "0.5.0", 4 | "mode": "development", 5 | "description": "h265ize is a fire and forget weapon. A nodejs utility utilizing ffmpeg to encode large quantities of videos with the hevc codec.", 6 | "main": "h265ize", 7 | "scripts": { 8 | "test": "./node_modules/.bin/mocha --reporter spec test.js" 9 | }, 10 | "bin": "h265ize", 11 | "directories": { 12 | "lib": "lib" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/FallingSnow/h265ize.git" 17 | }, 18 | "keywords": [ 19 | "video", 20 | "convert", 21 | "encode", 22 | "library", 23 | "transcode" 24 | ], 25 | "author": "Ayrton Sparling", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/FallingSnow/h265ize/issues" 29 | }, 30 | "homepage": "https://github.com/FallingSnow/h265ize#readme", 31 | "dependencies": { 32 | "bluebird": "^3.4.6", 33 | "chokidar": "^1.6.1", 34 | "colors": "^1.1.2", 35 | "filesize": "^3.3.0", 36 | "fluent-ffmpeg": "^2.1.0", 37 | "fs-extra": "^1.0.0", 38 | "gpu-info": "0.0.1", 39 | "hasbin": "^1.2.3", 40 | "keypress": "^0.2.1", 41 | "local-git-sync": "^1.8.3", 42 | "lodash": "^4.16.6", 43 | "long-stack-traces": "^0.1.2", 44 | "mathjs": "^3.7.0", 45 | "memorystream": "^0.3.1", 46 | "mime": "^1.3.4", 47 | "moment": "^2.16.0", 48 | "moment-duration-format": "^1.3.0", 49 | "optional": "^0.1.3", 50 | "promise-pauser": "^1.0.0", 51 | "recursive-readdir": "^2.1.0", 52 | "stack-trace": "0.0.9", 53 | "strip-ansi": "^3.0.1", 54 | "winston": "^2.3.0", 55 | "yargs": "^6.3.0" 56 | }, 57 | "devDependencies": { 58 | "chai": "^3.5.0", 59 | "mocha": "^3.1.2" 60 | }, 61 | "eslintConfig": { 62 | "parserOptions": { 63 | "ecmaVersion": 6, 64 | "sourceType": "module" 65 | }, 66 | "env": { 67 | "node": true 68 | }, 69 | "rules": { 70 | "strict": [ 71 | 2, 72 | "never" 73 | ] 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const h265ize = require('./h265ize'); 3 | 4 | const testVideoPath = 'test/sintel-test.mkv'; 5 | 6 | const nullFunc = function() { 7 | return undefined; 8 | } 9 | const nullLogger = { 10 | debug: nullFunc, 11 | verbose: nullFunc, 12 | info: nullFunc, 13 | error: nullFunc, 14 | alert: nullFunc, 15 | warn: nullFunc 16 | } 17 | 18 | let Encoder, Video; 19 | describe('Encoder', function() { 20 | this.timeout(1000); 21 | it('should return an instance of Encoder', function() { 22 | assert.instanceOf(Encoder = new h265ize.Encoder(nullLogger), h265ize.Encoder, 'new h265ize.Encoder() did not return an instance of h265ize.Encoder'); 23 | }); 24 | it('should add a video by path', function() { 25 | assert.includeDeepMembers(Encoder.queue, [Encoder.addVideo(testVideoPath)], 'video not added'); 26 | }); 27 | it('should start', function() { 28 | assert.ifError(Encoder.start()); 29 | assert.isTrue(Encoder.running, 'encoder did not start'); 30 | }); 31 | it('should pause', function() { 32 | assert.ifError(Encoder.pause()); 33 | assert.isTrue(Encoder.paused, 'encoder did not pause'); 34 | }); 35 | it('should resume', function() { 36 | assert.ifError(Encoder.resume()); 37 | assert.isFalse(Encoder.paused, 'encoder is still paused'); 38 | }); 39 | it('should stop', function() { 40 | assert.ifError(Encoder.stop()); 41 | assert.isFalse(Encoder.running, 'encoder is still running'); 42 | }); 43 | }); 44 | describe('Video', function() { 45 | this.timeout(1000); 46 | it('should return an instance of Video', function() { 47 | assert.doesNotThrow(function() { 48 | return new h265ize.Video(testVideoPath); 49 | }, "Error while initializing h265ize.Video"); 50 | assert.instanceOf(Video = new h265ize.Video(testVideoPath), h265ize.Video, 'new h265ize.Encoder() is not an instance of h265ize.Encoder'); 51 | }); 52 | it('should add video to encoder', function() { 53 | Encoder.addVideo(Video); 54 | assert.includeDeepMembers(Encoder.queue, [Video], 'video not added'); 55 | }); 56 | it('should start', function() { 57 | assert.ifError(Video.start()); 58 | assert.isTrue(Video.running, 'video did not start'); 59 | }); 60 | it('should pause', function() { 61 | assert.ifError(Video.pause()); 62 | assert.isTrue(Video.paused, 'video did not pause'); 63 | }); 64 | it('should resume', function() { 65 | assert.ifError(Video.resume()); 66 | assert.isFalse(Video.paused, 'video is still paused'); 67 | }); 68 | it('should stop', function() { 69 | assert.ifError(Video.stop()); 70 | assert.isFalse(Video.running, 'video is still running'); 71 | }); 72 | it('should encode a video', function() { 73 | let options = { 74 | 75 | }; 76 | function handler() { 77 | assert.isTrue(Video.status === 'finished'); 78 | } 79 | 80 | Video = new h265ize.Video(testVideoPath, options) 81 | .on('finished', handler) 82 | .on('failed', handler); 83 | }); 84 | it('should replace original video', function() { 85 | 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | const winston = require("winston"); 2 | const Path = require("path"); 3 | const colors = require('colors'); 4 | const stripAnsi = require('strip-ansi'); 5 | const readline = require("readline"); 6 | const keypress = require('keypress'); 7 | 8 | const packageSettings = require(Path.join(__dirname, '../package.json')); 9 | const helpers = require(Path.join(__dirname, './helpers.js')); 10 | 11 | let logFile = Path.join(process.cwd(), 'h265ize.log'); 12 | 13 | let logLevels = { 14 | levels: { 15 | error: 0, 16 | warn: 1, 17 | alert: 2, 18 | info: 3, 19 | verbose: 4, 20 | debug: 5 21 | }, 22 | colors: { 23 | error: 'red', 24 | warn: 'yellow', 25 | alert: 'magenta', 26 | info: 'white', 27 | verbose: 'cyan', 28 | debug: 'grey' 29 | } 30 | }; 31 | winston.addColors(logLevels.colors); 32 | module.exports = function (level) { 33 | let lastLineWasCleared = false; 34 | let lastMessageLength = 0; 35 | let keypressListener; 36 | let logger = new (winston.Logger)({ 37 | levels: logLevels.levels, 38 | transports: [ 39 | new (winston.transports.Console)({ 40 | name: 'default-logger', 41 | level: level, 42 | colorize: true, 43 | label: packageSettings.name, 44 | prettyPrint: true, 45 | //handleExceptions: true, 46 | //humanReadableUnhandledException: true, 47 | // timestamp: function() { 48 | // return Date.now(); 49 | // }, 50 | formatter: function (options) { 51 | let label = options.level === 'error' ? colors[logLevels.colors['error']]('[' + options.label + ']') : colors.green('[' + options.label + ']'); 52 | let logLevel = (options.level === 'info' || options.level === 'error') ? '' : colors[logLevels.colors[options.level]]('[' + options.level + ']') + ' '; 53 | return label + ': ' + logLevel + 54 | (undefined !== options.message ? options.message : '') + 55 | (options.meta && Object.keys(options.meta).length ? helpers.createListString(options.meta) : ''); 56 | } 57 | }), 58 | ], 59 | filters: [ 60 | function (level, msg, meta) { 61 | if (meta.__divider) 62 | msg = msg + '\n' + '-'.repeat(process.stdout.columns); 63 | 64 | delete meta.__clearLine; 65 | delete meta.__divider; 66 | return msg; 67 | } 68 | ], 69 | rewriters: [ 70 | function (level, msg, meta) { 71 | if (meta.__clearLine && lastLineWasCleared) { 72 | readline.moveCursor(process.stdout, -1000, -Math.ceil(lastMessageLength / process.stdout.columns)); 73 | readline.clearLine(process.stdout, 0); 74 | lastLineWasCleared = true; 75 | } else if (meta.__clearLine) { 76 | lastLineWasCleared = true; 77 | } else { 78 | lastLineWasCleared = false; 79 | } 80 | lastMessageLength = stripAnsi(msg).length; 81 | 82 | return meta; 83 | } 84 | ] 85 | }); 86 | 87 | if (level === 'debug') { 88 | logger.debug('Log file location:', logFile); 89 | logger.add(winston.transports.File, { 90 | filename: logFile, 91 | level: level, 92 | label: packageSettings.name, 93 | prettyPrint: true, 94 | json: false, 95 | timestamp: false, 96 | formatter: function (options) { 97 | let label = '[' + options.label + ']'; 98 | let logLevel = (options.level === 'info' || options.level === 'error') ? '' : '[' + options.level + ']' + ' '; 99 | return stripAnsi(label + ': ' + logLevel + 100 | (undefined !== options.message ? options.message : '') + 101 | (options.meta && Object.keys(options.meta).length ? helpers.createListString(options.meta) : '')); 102 | } 103 | }); 104 | } 105 | 106 | if (module.parent && module.parent.name === 'h265ize') { 107 | if (process.stdin.isTTY) { 108 | keypress(process.stdin); 109 | process.stdin.setRawMode(true); 110 | process.stdin.resume(); 111 | 112 | if (keypressListener === true) 113 | process.stdin.removeListener('keypress', keypressListener); 114 | 115 | keypressListener = process.stdin.on('keypress', function (ch, key) { 116 | if (key && key.name == 'd') { 117 | let logLevelIsDebug = (logger.transports['default-logger'].level === 'debug'); 118 | logger.info('Debugging', logLevelIsDebug ? 'disabled.' : 'enabled.'); 119 | if (logLevelIsDebug) { 120 | logger.transports['default-logger'].level = 'info'; 121 | } else { 122 | logger.transports['default-logger'].level = 'debug'; 123 | } 124 | } 125 | }); 126 | } 127 | } 128 | 129 | return logger; 130 | }; 131 | -------------------------------------------------------------------------------- /lib/classes/encoder.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const os = require('os'); 3 | const readline = require('readline'); 4 | const EventEmitter = require('events'); 5 | 6 | const _ = require('lodash'); 7 | const colors = require('colors'); 8 | const moment = require('moment'); 9 | require('moment-duration-format'); 10 | const filesize = require('filesize'); 11 | const fs = require('fs-extra'); 12 | const gpuInfo = require('gpu-info'); 13 | // const hasbin = require('hasbin'); 14 | 15 | const Video = require('./video.js'); 16 | const consoleLogger = require('../consoleLogger.js'); 17 | 18 | class Encoder { 19 | constructor(logger, options) { 20 | this.logger = logger || consoleLogger; 21 | this.queue = []; 22 | this.currentlyProcessing; 23 | this.failedVideos = []; 24 | this.finishedVideos = []; 25 | this.potentialHWAccelSupport = false; 26 | this.supportedHWAccel = []; 27 | this.watchIgnore = []; 28 | this.enablePreviewStream = false; 29 | if (options) { 30 | if (options.enablePreviewStream) 31 | this.enablePreviewStream = options.enablePreviewStream; 32 | } 33 | 34 | this.running = false; 35 | this.paused = false; 36 | 37 | this.events = new EventEmitter(); 38 | 39 | // Check for gpu encoding support 40 | let _self = this; 41 | gpuInfo().then(function(data) { 42 | if (os.platform() === 'win32') 43 | for (let gpu of data) { 44 | if (gpu.AdapterCompatibility === 'NVIDIA') { 45 | _self.potentialHWAccelSupport = true; 46 | _self.logger.verbose('NVIDIA GPU Detected. Hardware Accelerated encoding support unlocked.') 47 | } 48 | if (gpu.AdapterCompatibility === 'Intel Corporation') { 49 | _self.potentialHWAccelSupport = true; 50 | _self.logger.verbose('Intel GPU Detected. Hardware Accelerated encoding support unlocked.') 51 | } 52 | } 53 | }).catch(function(err) { 54 | _self.logger.debug('GPU detection error:', err.message); 55 | }); 56 | } 57 | 58 | start() { 59 | if (this.running) 60 | return new Error('ALREADYRUNNING'); 61 | 62 | if (this.paused) { 63 | this.paused = false; 64 | this.running = true; 65 | if (this.currentlyProcessing) { 66 | this.currentlyProcessing.start(); 67 | } else { 68 | this.loop(); 69 | } 70 | this.events.emit('running'); 71 | } else { 72 | this.running = true; 73 | this.loop(); 74 | this.events.emit('running'); 75 | } 76 | 77 | } 78 | 79 | resume() { 80 | return this.start(); 81 | } 82 | 83 | pause() { 84 | if (this.paused) { 85 | return new Error('ALREADYPAUSED'); 86 | } 87 | 88 | if (!this.running) { 89 | return new Error('NOTRUNNING'); 90 | } 91 | 92 | if (this.currentlyProcessing) { 93 | this.currentlyProcessing.pause(); 94 | } 95 | 96 | this.running = false; 97 | this.paused = true; 98 | 99 | this.events.emit('paused'); 100 | } 101 | 102 | loop() { 103 | if (!this.running) 104 | return; 105 | 106 | // Get first video in queue to encode 107 | let video = this.currentlyProcessing = this.queue.shift(); 108 | 109 | // All videos have been encoded 110 | if (typeof video === 'undefined') { 111 | 112 | // Notify user of which videos have not been encoded 113 | let numVideosFailed = _.keys(this.rejectedVideos).length; 114 | if (numVideosFailed) 115 | this.logger.alert('The following videos', colors.yellow('(' + numVideosFailed + ')'), 'were not encoded:', this.failedVideos); 116 | 117 | return this.events.emit('finished'); 118 | } 119 | 120 | this.events.emit('processing', video); 121 | 122 | this.logger.info('Processing', colors.bold(colors.yellow(video.base)) + "...", { 123 | __divider: true 124 | }); 125 | 126 | let _self = this; 127 | video.events 128 | .on('finished', function() { 129 | // Notify user that all videos have been encoded 130 | _self.logger.info('Video finished processing at', colors.yellow(moment().format('dddd, MMMM Do YYYY, h:mm:ss A')), { 131 | __divider: true 132 | }); 133 | 134 | _self.finishedVideos.push(video); 135 | _self.removeVideo(video); 136 | _self.loop(); 137 | }) 138 | .on('failed', function(video) { 139 | // Notify user that all videos have been encoded 140 | _self.logger.info('Video failed processing at', colors.yellow(moment().format('dddd, MMMM Do YYYY, h:mm:ss A')), { 141 | __divider: true 142 | }); 143 | 144 | _self.failedVideos.push(video); 145 | _self.logger.error(video.error.message + '. Details can be found below in debug mode.'); 146 | _self.logger.debug(video.error.stack); 147 | _self.removeVideo(video); 148 | _self.loop(); 149 | }); 150 | 151 | video.start(); 152 | } 153 | 154 | addVideo(path, options) { 155 | 156 | // Check if file exists 157 | 158 | let video; 159 | if (path instanceof Video) { 160 | video = path; 161 | } else { 162 | fs.accessSync(path, fs.F_OK); 163 | video = new Video(path, options); 164 | } 165 | video.encoder = this; 166 | this.queue.push(video); 167 | 168 | if (this.running && !this.paused && !this.currentlyProcessing) { 169 | this.loop(); 170 | } 171 | return video; 172 | } 173 | 174 | removeVideo(video) { 175 | if (video.running) { 176 | video.stop(); 177 | } 178 | for (let i in this.queue) { 179 | if (this.queue[i].id === video.id) { 180 | this.queue.splice(i, 1); 181 | break; 182 | } 183 | } 184 | } 185 | 186 | stop() { 187 | if (!this.running) { 188 | return new Error('NOTRUNNING'); 189 | } 190 | 191 | this.running = false; 192 | this.paused = false; 193 | if (this.currentlyProcessing) 194 | this.currentlyProcessing.stop(); 195 | this.currentlyProcessing = undefined; 196 | this.events.emit('stopped'); 197 | } 198 | } 199 | 200 | module.exports = Encoder; 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Unmaintained 2 | h265ize hasn't been maintained for a long time and there are no plans to maintain/update it in the future. 3 | 4 | # h265ize 5 | 6 | h265ize is a fire and forget weapon. A nodejs utility utilizing ffmpeg to encode large quantities of videos with the hevc codec. For more information visit [ayrton.sparling.us](https://ayrton.sparling.us/index.php/ultimate-x265hevc-encoding-script-h265ize/ "Ayrton Sparling"). 7 | 8 | [![NPM License](https://img.shields.io/npm/l/h265ize.svg)](https://raw.githubusercontent.com/FallingSnow/h265ize/master/LICENSE) [![NPM Version](https://img.shields.io/npm/v/h265ize.svg)](https://www.npmjs.com/package/h265ize) 9 | 10 | [![NPM Version](https://nodei.co/npm/h265ize.png)](https://www.npmjs.com/package/h265ize) 11 | 12 | If you have any questions or h265ize isn't working for you, feel free to open an issue. 13 | 14 | > _h265ize will support [AV1](https://en.wikipedia.org/wiki/AOMedia_Video_1) once encoder support becomes stable & plex supports decoding it._ 15 | 16 | ## Features 17 | 18 | - Works on Windows, OSX, and Linux 19 | - Batch file processing (can process a whole folder) 20 | - Automatically detects video files (only processes video files found within a folder) 21 | - Detects all audio tracks 22 | - Preserves audio codecs 23 | - Preserves audio track titles 24 | - Detects and preserves all subtitles 25 | - Detects audio language, if audio language is not your native language and native language subtitles are provided, makes those subtitles default 26 | - Automatically upconvert vobsub/dvdsubs to srt subtitles on mkv files 27 | - Detects bit depth and uses appropriate encoder profile (10-bit is common in high quality anime, supports 8-bit, 10-bit, 12-bit) 28 | - Verbose and preview mode 29 | - File overwrite detection (doesn't accidentally write over a file that already exists, other than in preview mode) 30 | - Detects if file is already encoded in x265 and skips it 31 | - Ability to make encoding previews 32 | - Take screenshots of a finished encode 33 | - Faulty encoding detection based on before and after video durations 34 | - Maintains file structure in output folder (So in theory you could just take your 3tb movie folder and throw it into the script and the output folder should look that same but with x265 videos) 35 | 36 | ## Dependencies 37 | 38 | - [Node.js](https://nodejs.org/en/) - Required in order to run h265ize. 39 | - [ffmpeg](https://ffmpeg.org/) - Does the video conversion among other things. 40 | 41 | ### Option Dependencies 42 | 43 | - [mkvtoolnix](https://www.bunkus.org/videotools/mkvtoolnix/) - Used for upconverting subs in MKVs. 44 | - [vobsub2srt](https://github.com/ruediger/VobSub2SRT) - Used for upconverting subs. 45 | 46 | ## Installation 47 | 48 | To install h265ize run one of the following command lines to download and install. 49 | 50 | ### Stable 51 | 52 | ``` 53 | npm install h265ize --global 54 | ``` 55 | 56 | ### Bleeding Edge 57 | 58 | ``` 59 | npm install FallingSnow/h265ize --global 60 | h265ize --version 61 | ``` 62 | 63 | ## Updating 64 | 65 | Simply run `npm install h265ize --global` again. 66 | 67 | ## Uninstalling 68 | 69 | `npm uninstall h265ize --global` 70 | 71 | ## Usage 72 | 73 | `./h265ize [--help] [-d ] [-q <0-51>] [-m ] [-n ] [-f {3}] [-g ] [-l ] [-o] [-p] [-v] [--bitdepth (8|10|12)] [--accurate-timestamps] [--as-preset ] [--disable-upconvert] [--debug] [--video-bitrate ] [--he-audio] [--force-he-audio] [--downmix-he-audio] [--screenshots] [--delete] ` 74 | 75 | ### Options 76 | 77 | > -d :Destination folder 78 | 79 | > -f :Container format to output; Options: mkv, mp4, m4v; default: mkv. 80 | 81 | > -l :Milliseconds to be encoded in preview mode; default: 30000 82 | 83 | > -m :x265 encoder preset; Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo; default: fast 84 | 85 | > -n :The native language used to select default audio and subtitles. You may use 3 letter or 2 letter ISO 639-2 Alpha-3/Alpha-2 codes or the full language name. Examples: [eng|en|English|jpn|ja|Japanese] 86 | 87 | > -o :Override mode; Allows conversion of videos that are already encoded by the hevc codec 88 | 89 | > -p :Preview mode; Only process a 30 second preview 90 | 91 | > -q :Sets the qp quality target; default: 19 92 | 93 | > -v :Verbose mode; Display extra output 94 | 95 | > -x :Extra x265 options. Options can be found on the [x265 options page](https://x265.readthedocs.io/en/stable/cli.html) 96 | 97 | > --bitdepth :Forces the output bitdepth (bitdepths 8, 10, and 12 are supported) 98 | 99 | > --accurate-timestamps :Accurate Timestamps (substantially increases file size but sometimes fixes timestamps) 100 | 101 | > --as-preset :My personal presets; Possible values are listed below; I'll be adding more as time goes on 102 | 103 | > --debug :Debug mode; Print extra debugging information 104 | 105 | > --delete :Deletes source after encoding is complete and replaces it with new encode; STRONGLY NOT RECOMMENDED 106 | 107 | > --disable-upconvert :Disable Upconvert; Stop converting Vobsub subs to srt; Only works with mkv's 108 | 109 | > --force-he-audio :Force High Efficiency audio encoding even on lossless audio tracks 110 | 111 | > --he-audio :High Efficiency audio mode 112 | 113 | > --downmix-he-audio :If there are more than 2.1 audio channels, downmix them to stereo. 114 | 115 | > --normalize-level :Define a level of normalization to be applied. See [Issue 56](https://github.com/FallingSnow/h265ize/issues/56) for more info. 116 | 117 | > --screenshots :Take 6 screenshots at regular intervals throughout the finished encode 118 | 119 | > --stats: Creates a stats file in the current working directory named h265ize.csv 120 | 121 | > --watch: Watches a folder for new files and process the videos 122 | 123 | > --video-bitrate :Sets the video bitrate, set to 0 to use qp instead of a target bitrate 124 | 125 | > --test: Test mode; Runs as normal, but do not encode any files 126 | 127 | > --help :Help; Shows help page 128 | 129 | > --version: Show version information 130 | 131 | Run `h265ize --help` for more info. 132 | 133 | ### Aspresets 134 | 135 | Preset | Description 136 | :----------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- 137 | anime-high | A very good preset for all types of anime. Produces very good quality for a very small size. 138 | anime-medium | Same as anime-high but uses debanding to produce better color gradients. 139 | testing-ssim | x265's native preset just in SSIM mode. 140 | 141 | ### Examples 142 | 143 | - `h265ize -v big_buck_bunny_1080p_h264.mov` 144 | - `h265ize -v -d /home -q 25 big_buck_bunny_folder` 145 | - `h265ize -d /home -q 25 --watch videos/folder` 146 | 147 | ## Stats file 148 | 149 | The stats file is located at the current working directory under the name `h265ize.csv`. This must be enabled using the `--stats` flag. The file is composed of several lines. Each line is in the format 150 | 151 | `[Finish Encoding Date],[File Path],[Original Size],[Encoded size],[Compression Precent],[Encoding Duration]` 152 | 153 | For example: 154 | 155 | `08/13 02:46:03 PM, videos/[deanzel] Noir - 08 [BD 1080p Hi10p Dual Audio FLAC][a436a4e8].mkv, 1964MB, 504MB, 25.66%, 2:51:16` 156 | 157 | ## Creating 10bit & 12bit encodes 158 | 159 | To create 10 or 12bit encodes, simply pass the `--bitdepth 10` or `--bitdepth 12` parameters. Make sure you have the correct libraries or ffmpeg static build. 160 | -------------------------------------------------------------------------------- /h265ize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* global __dirname */ 4 | 5 | const Path = require('path'); 6 | const os = require("os"); 7 | const readline = require("readline"); 8 | 9 | const _ = require("lodash"); 10 | const colors = require('colors'); 11 | const moment = require('moment'); 12 | require("moment-duration-format"); 13 | const fs = require('fs-extra'); 14 | const keypress = require('keypress'); 15 | const Promise = require("bluebird"); 16 | const Pauser = require('promise-pauser'); 17 | //const stackTrace = require('stack-trace'); 18 | //require("long-stack-traces"); 19 | const yargs = require('yargs'); 20 | const git = require('local-git-sync'); 21 | const chokidar = require('chokidar'); 22 | 23 | const helpers = require(Path.join(__dirname, 'lib/helpers.js')); 24 | const packageSettings = require(Path.join(__dirname, 'package.json')); 25 | const h265izeLogger = require('./lib/logger.js'); 26 | 27 | /* INIT */ 28 | 29 | module.name = packageSettings.name; 30 | 31 | Promise.config({ 32 | // Enable warnings 33 | warnings: true, 34 | // Enable long stack traces 35 | longStackTraces: true, 36 | // Enable cancellation 37 | cancellation: true, 38 | // Enable monitoring 39 | monitoring: false 40 | }); 41 | 42 | process.H265IZE_STOP_REQUEST_COUNT = 0; 43 | 44 | /* --------------------------------- CLASSES -------------------------------- */ 45 | 46 | const Video = require('./lib/classes/video.js'); 47 | const Encoder = require('./lib/classes/encoder.js'); 48 | 49 | /* ------------------------------ END CLASSES ------------------------------- */ 50 | 51 | let h265ize = { 52 | Encoder: Encoder, 53 | Video: Video, 54 | _helpers: helpers 55 | }; 56 | 57 | /* ------------------------ Command Line Interface -------------------------- */ 58 | 59 | function runCli() { 60 | let logger; 61 | 62 | new Promise(function(resolve, reject) { 63 | let args = helpers.getCLIArguments(); 64 | 65 | // Initialize a new logger. This can be replaced by the console logger, 66 | // for example: 67 | // const consoleLogger = require('./lib/consoleLogger.js'); 68 | // let logger = require('./lib/consoleLogger.js'); 69 | if (args.debug) 70 | logger = h265izeLogger('debug'); 71 | else if (args.verbose) 72 | logger = h265izeLogger('verbose'); 73 | else 74 | logger = h265izeLogger('info'); 75 | 76 | logger.debug('Command Line: ' + process.argv.join(' ')); 77 | 78 | // Show help 79 | if (args.help) { 80 | console.log(colors.underline('Package:'), colors.yellow(packageSettings.name), '\t', colors.underline('Version:'), colors.yellow(packageSettings.version)); 81 | console.log(colors.underline('Description:'), packageSettings.description); 82 | yargs.showHelp(); 83 | process.exit(0); 84 | } 85 | 86 | // Show version 87 | if (args.version) { 88 | if (packageSettings.mode === 'production') { 89 | console.log(packageSettings.name, packageSettings.version); 90 | process.exit(0); 91 | } else if (packageSettings.mode === 'development') { 92 | let branch = 'unknown'; 93 | let short = "unknown"; 94 | if (packageSettings.gitHead && packageSettings._requested) { 95 | let url = packageSettings._requested.hosted.directUrl; 96 | branch = packageSettings._requested.hosted.directUrl.substring(url.lastIndexOf('/', url.length - 14), url.length - 13).substr(1, url.length); 97 | short = packageSettings.gitHead.substr(0, 7); 98 | } else { 99 | try { 100 | branch = git.branch(__dirname); 101 | short = git.short(__dirname); 102 | } catch (e) { 103 | console.log('Unabled to retrieve version:', e); 104 | process.exit(1); 105 | } 106 | } 107 | console.log(packageSettings.name, branch + '#' + short); 108 | process.exit(0); 109 | } 110 | } else { 111 | resolve(args); 112 | } 113 | }).then(function(args) { 114 | 115 | // Initiate a new Encoder 116 | let encoder = new Encoder(logger); 117 | 118 | function shutdown() { 119 | encoder.stop(); 120 | if (typeof watcher !== 'undefined') 121 | watcher.close(); 122 | 123 | if (process.H265IZE_STOP_REQUEST_COUNT > 1) { 124 | logger.error('Shutdown signal more than once. Shutting down immediately...') 125 | return process.exit(1); 126 | } 127 | 128 | if (logger.transports.file) { 129 | // File logger needs time to flush to disk 130 | logger.info('Flushing log to disk...'); 131 | setTimeout(function() { 132 | logger.info('Process ended.'); 133 | process.exit(0); 134 | }, 2000); 135 | } else { 136 | logger.info('Process ended.'); 137 | process.exit(0); 138 | } 139 | } 140 | 141 | // Make sure this is a terminal where we can listen for input events 142 | // If it is, then listen for specific keypress events 143 | if (process.stdin.isTTY) { 144 | keypress(process.stdin); 145 | process.stdin.setRawMode(true); 146 | process.stdin.resume(); 147 | process.stdin.on('keypress', function(ch, key) { 148 | if (key && key.ctrl && key.name == 'c') { 149 | readline.clearLine(process.stdout, 0); 150 | readline.moveCursor(process.stdout, -1000, 0); 151 | logger.warn('Caught signal interupt! Attempted to clean up.'); 152 | process.H265IZE_STOP_REQUEST_COUNT++; 153 | shutdown(); 154 | } else if (key && key.name == 'p') { 155 | 156 | if (!encoder.paused && encoder.running) { 157 | encoder.pause(); 158 | } else if (encoder.paused && !encoder.running) { 159 | encoder.resume(); 160 | } else if (!encoder.running) { 161 | logger.error('Encoder could not be paused because it is not running.'); 162 | } 163 | } 164 | }); 165 | } 166 | 167 | // Watch for kill signals 168 | process.on('SIGINT', function() { 169 | logger.warn('Caught signal interupt! Attempted to clean up.'); 170 | shutdown(); 171 | }); 172 | process.on('uncaughtException', (err) => { 173 | logger.error(err); 174 | shutdown(); 175 | }); 176 | 177 | // Check if an input was given (an argument without a preceding flag that is passed a 178 | // parameter) 179 | let input = args._[0]; 180 | if (args.watch) { 181 | watchDirectory(args.watch); 182 | } else if (!input) { 183 | yargs.showHelp(); 184 | process.exit(0); 185 | } else { 186 | processInput(input); 187 | } 188 | 189 | function watchDirectory(path) { 190 | logger.info('Switching to watch mode...'); 191 | encoder.root = path; 192 | 193 | watcher = chokidar.watch(args.watch, { 194 | ignored: /[\/\\]\./, 195 | persistent: true, 196 | awaitWriteFinish: { 197 | stabilityThreshold: 3000, 198 | pollInterval: 1000 199 | }, 200 | ignoreInitial: true 201 | }); 202 | watcher.on('add', (path) => { 203 | if (pathHasBeenProccessed(path)) { 204 | return logger.debug(path, 'was added to watch folder but has already been processed. Ignoring...'); 205 | } 206 | helpers.parseInput(path, logger).then(function(paths) { 207 | // Take each video path that we were given and add it to our Encoder 208 | _.each(paths, function(path, i) { 209 | encoder.addVideo(path, Object.assign({}, args, { 210 | destination: Path.resolve(args.destination, Path.dirname(Path.relative(encoder.root, path))) 211 | })); 212 | }); 213 | }); 214 | }); 215 | 216 | logger.info('Watching', args.watch + '...'); 217 | 218 | // Start the encoder 219 | encoder.start(); 220 | } 221 | 222 | function pathHasBeenProccessed(path) { 223 | for (let processed of encoder.watchIgnore) { 224 | if (path === processed) 225 | return true; 226 | } 227 | return false; 228 | } 229 | 230 | function processInput(path) { 231 | encoder.root = path; 232 | 233 | // Detect if the input exists and whether it is a file or directory 234 | helpers.parseInput(input, logger) 235 | 236 | .then(function(paths) { 237 | 238 | // Check if any video paths were returned 239 | if (paths.length < 1) { 240 | logger.error('Could not retrieve input paths.'); 241 | return process.exit(1); 242 | } 243 | 244 | // Take each video path that we were given and add it to our new Encoder 245 | _.each(paths, function(path, i) { 246 | encoder.addVideo(path, Object.assign({}, args, { 247 | destination: Path.resolve(args.destination, Path.dirname(Path.relative(encoder.root, path))) 248 | })); 249 | }); 250 | 251 | // Tell the user when the encoder has started 252 | logger.verbose('Encoding started at', colors.yellow(moment().format("dddd, MMMM Do YYYY, h:mm:ss A"))); 253 | 254 | // Start the encoder 255 | encoder.start(); 256 | 257 | // Output some information when the Encoder finishes encoding its queue 258 | encoder.events.on('finished', function() { 259 | 260 | logger.verbose('Folder encoding finished at', colors.yellow(moment().format("dddd, MMMM Do YYYY, h:mm:ss A")), { 261 | __divider: true 262 | }); 263 | 264 | if (encoder.failedVideos.length) 265 | logger.alert('The following videos', colors.yellow('(' + encoder.failedVideos.length + ')'), 'were not encoded:', _.flatMap(encoder.failedVideos, function(v) { 266 | return colors.yellow(v.base) + ': ' + v.error; 267 | })); 268 | 269 | shutdown(); 270 | }); 271 | }); 272 | } 273 | }); 274 | 275 | } 276 | 277 | if (!module.parent) { 278 | runCli(); 279 | } 280 | /* ---------------------- END Command Line Interface ------------------------ */ 281 | 282 | module.exports = h265ize; 283 | -------------------------------------------------------------------------------- /lib/languages.json: -------------------------------------------------------------------------------- 1 | { 2 | "alpha2Languages": { 3 | "aa": "Afar", 4 | "ab": "Abkhazian", 5 | "ae": "Avestan", 6 | "af": "Afrikaans", 7 | "ak": "Akan", 8 | "am": "Amharic", 9 | "an": "Aragonese", 10 | "ar": "Arabic", 11 | "as": "Assamese", 12 | "av": "Avaric", 13 | "ay": "Aymara", 14 | "az": "Azerbaijani", 15 | "ba": "Bashkir", 16 | "be": "Belarusian", 17 | "bg": "Bulgarian", 18 | "bh": "Bihari languages", 19 | "bi": "Bislama", 20 | "bm": "Bambara", 21 | "bn": "Bengali", 22 | "bo": "Tibetan", 23 | "br": "Breton", 24 | "bs": "Bosnian", 25 | "ca": "Catalan; Valencian", 26 | "ce": "Chechen", 27 | "ch": "Chamorro", 28 | "co": "Corsican", 29 | "cr": "Cree", 30 | "cs": "Czech", 31 | "cu": "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", 32 | "cv": "Chuvash", 33 | "cy": "Welsh", 34 | "da": "Danish", 35 | "de": "German", 36 | "dv": "Divehi; Dhivehi; Maldivian", 37 | "dz": "Dzongkha", 38 | "ee": "Ewe", 39 | "el": "Greek: Modern (1453-)", 40 | "en": "English", 41 | "eo": "Esperanto", 42 | "es": "Spanish; Castilian", 43 | "et": "Estonian", 44 | "eu": "Basque", 45 | "fa": "Persian", 46 | "ff": "Fulah", 47 | "fi": "Finnish", 48 | "fj": "Fijian", 49 | "fo": "Faroese", 50 | "fr": "French", 51 | "fy": "Western Frisian", 52 | "ga": "Irish", 53 | "gd": "Gaelic; Scottish Gaelic", 54 | "gl": "Galician", 55 | "gn": "Guarani", 56 | "gu": "Gujarati", 57 | "gv": "Manx", 58 | "ha": "Hausa", 59 | "he": "Hebrew", 60 | "hi": "Hindi", 61 | "ho": "Hiri Motu", 62 | "hr": "Croatian", 63 | "ht": "Haitian; Haitian Creole", 64 | "hu": "Hungarian", 65 | "hy": "Armenian", 66 | "hz": "Herero", 67 | "ia": "Interlingua (International Auxiliary Language Association)", 68 | "id": "Indonesian", 69 | "ie": "Interlingue; Occidental", 70 | "ig": "Igbo", 71 | "ii": "Sichuan Yi; Nuosu", 72 | "ik": "Inupiaq", 73 | "io": "Ido", 74 | "is": "Icelandic", 75 | "it": "Italian", 76 | "iu": "Inuktitut", 77 | "ja": "Japanese", 78 | "jv": "Javanese", 79 | "ka": "Georgian", 80 | "kg": "Kongo", 81 | "ki": "Kikuyu; Gikuyu", 82 | "kj": "Kuanyama; Kwanyama", 83 | "kk": "Kazakh", 84 | "kl": "Kalaallisut; Greenlandic", 85 | "km": "Central Khmer", 86 | "kn": "Kannada", 87 | "ko": "Korean", 88 | "kr": "Kanuri", 89 | "ks": "Kashmiri", 90 | "ku": "Kurdish", 91 | "kv": "Komi", 92 | "kw": "Cornish", 93 | "ky": "Kirghiz; Kyrgyz", 94 | "la": "Latin", 95 | "lb": "Luxembourgish; Letzeburgesch", 96 | "lg": "Ganda", 97 | "li": "Limburgan; Limburger; Limburgish", 98 | "ln": "Lingala", 99 | "lo": "Lao", 100 | "lt": "Lithuanian", 101 | "lu": "Luba-Katanga", 102 | "lv": "Latvian", 103 | "mg": "Malagasy", 104 | "mh": "Marshallese", 105 | "mi": "Maori", 106 | "mk": "Macedonian", 107 | "ml": "Malayalam", 108 | "mn": "Mongolian", 109 | "mr": "Marathi", 110 | "ms": "Malay", 111 | "mt": "Maltese", 112 | "my": "Burmese", 113 | "na": "Nauru", 114 | "nb": "Bokmål: Norwegian; Norwegian Bokmål", 115 | "nd": "Ndebele: North; North Ndebele", 116 | "ne": "Nepali", 117 | "ng": "Ndonga", 118 | "nl": "Dutch; Flemish", 119 | "nn": "Norwegian Nynorsk; Nynorsk: Norwegian", 120 | "no": "Norwegian", 121 | "nr": "Ndebele: South; South Ndebele", 122 | "nv": "Navajo; Navaho", 123 | "ny": "Chichewa; Chewa; Nyanja", 124 | "oc": "Occitan (post 1500); Provençal", 125 | "oj": "Ojibwa", 126 | "om": "Oromo", 127 | "or": "Oriya", 128 | "os": "Ossetian; Ossetic", 129 | "pa": "Panjabi; Punjabi", 130 | "pi": "Pali", 131 | "pl": "Polish", 132 | "ps": "Pushto; Pashto", 133 | "pt": "Portuguese", 134 | "qu": "Quechua", 135 | "rm": "Romansh", 136 | "rn": "Rundi", 137 | "ro": "Romanian; Moldavian; Moldovan", 138 | "ru": "Russian", 139 | "rw": "Kinyarwanda", 140 | "sa": "Sanskrit", 141 | "sc": "Sardinian", 142 | "sd": "Sindhi", 143 | "se": "Northern Sami", 144 | "sg": "Sango", 145 | "si": "Sinhala; Sinhalese", 146 | "sk": "Slovak", 147 | "sl": "Slovenian", 148 | "sm": "Samoan", 149 | "sn": "Shona", 150 | "so": "Somali", 151 | "sq": "Albanian", 152 | "sr": "Serbian", 153 | "ss": "Swati", 154 | "st": "Sotho: Southern", 155 | "su": "Sundanese", 156 | "sv": "Swedish", 157 | "sw": "Swahili", 158 | "ta": "Tamil", 159 | "te": "Telugu", 160 | "tg": "Tajik", 161 | "th": "Thai", 162 | "ti": "Tigrinya", 163 | "tk": "Turkmen", 164 | "tl": "Tagalog", 165 | "tn": "Tswana", 166 | "to": "Tonga (Tonga Islands)", 167 | "tr": "Turkish", 168 | "ts": "Tsonga", 169 | "tt": "Tatar", 170 | "tw": "Twi", 171 | "ty": "Tahitian", 172 | "ug": "Uighur; Uyghur", 173 | "uk": "Ukrainian", 174 | "ur": "Urdu", 175 | "uz": "Uzbek", 176 | "ve": "Venda", 177 | "vi": "Vietnamese", 178 | "vo": "Volapük", 179 | "wa": "Walloon", 180 | "wo": "Wolof", 181 | "xh": "Xhosa", 182 | "yi": "Yiddish", 183 | "yo": "Yoruba", 184 | "za": "Zhuang; Chuang", 185 | "zh": "Chinese", 186 | "zu": "Zulu", 187 | "un": "Unknown" 188 | }, 189 | "alpha3Languages": { 190 | "aar": "Afar", 191 | "abk": "Abkhazian", 192 | "afr": "Afrikaans", 193 | "aka": "Akan", 194 | "alb": "Albanian", 195 | "amh": "Amharic", 196 | "ara": "Arabic", 197 | "arg": "Aragonese", 198 | "arm": "Armenian", 199 | "asm": "Assamese", 200 | "ava": "Avaric", 201 | "ave": "Avestan", 202 | "aym": "Aymara", 203 | "aze": "Azerbaijani", 204 | "bak": "Bashkir", 205 | "bam": "Bambara", 206 | "baq": "Basque", 207 | "bel": "Belarusian", 208 | "ben": "Bengali", 209 | "bih": "Bihari languages", 210 | "bis": "Bislama", 211 | "bod": "Tibetan", 212 | "bos": "Bosnian", 213 | "bre": "Breton", 214 | "bul": "Bulgarian", 215 | "bur": "Burmese", 216 | "cat": "Catalan; Valencian", 217 | "ces": "Czech", 218 | "cha": "Chamorro", 219 | "che": "Chechen", 220 | "chi": "Chinese", 221 | "chu": "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", 222 | "chv": "Chuvash", 223 | "cor": "Cornish", 224 | "cos": "Corsican", 225 | "cre": "Cree", 226 | "cym": "Welsh", 227 | "cze": "Czech", 228 | "dan": "Danish", 229 | "deu": "German", 230 | "div": "Divehi; Dhivehi; Maldivian", 231 | "dut": "Dutch; Flemish", 232 | "dzo": "Dzongkha", 233 | "ell": "Greek, Modern (1453-)", 234 | "eng": "English", 235 | "epo": "Esperanto", 236 | "est": "Estonian", 237 | "eus": "Basque", 238 | "ewe": "Ewe", 239 | "fao": "Faroese", 240 | "fas": "Persian", 241 | "fij": "Fijian", 242 | "fin": "Finnish", 243 | "fra": "French", 244 | "fre": "French", 245 | "fry": "Western Frisian", 246 | "ful": "Fulah", 247 | "geo": "Georgian", 248 | "ger": "German", 249 | "gla": "Gaelic; Scottish Gaelic", 250 | "gle": "Irish", 251 | "glg": "Galician", 252 | "glv": "Manx", 253 | "gre": "Greek, Modern (1453-)", 254 | "grn": "Guarani", 255 | "guj": "Gujarati", 256 | "hat": "Haitian; Haitian Creole", 257 | "hau": "Hausa", 258 | "heb": "Hebrew", 259 | "her": "Herero", 260 | "hin": "Hindi", 261 | "hmo": "Hiri Motu", 262 | "hrv": "Croatian", 263 | "hun": "Hungarian", 264 | "hye": "Armenian", 265 | "ibo": "Igbo", 266 | "ice": "Icelandic", 267 | "ido": "Ido", 268 | "iii": "Sichuan Yi; Nuosu", 269 | "iku": "Inuktitut", 270 | "ile": "Interlingue; Occidental", 271 | "ina": "Interlingua (International Auxiliary Language Association)", 272 | "ind": "Indonesian", 273 | "ipk": "Inupiaq", 274 | "isl": "Icelandic", 275 | "ita": "Italian", 276 | "jav": "Javanese", 277 | "jpn": "Japanese", 278 | "kal": "Kalaallisut; Greenlandic", 279 | "kan": "Kannada", 280 | "kas": "Kashmiri", 281 | "kat": "Georgian", 282 | "kau": "Kanuri", 283 | "kaz": "Kazakh", 284 | "khm": "Central Khmer", 285 | "kik": "Kikuyu; Gikuyu", 286 | "kin": "Kinyarwanda", 287 | "kir": "Kirghiz; Kyrgyz", 288 | "kom": "Komi", 289 | "kon": "Kongo", 290 | "kor": "Korean", 291 | "kua": "Kuanyama; Kwanyama", 292 | "kur": "Kurdish", 293 | "lao": "Lao", 294 | "lat": "Latin", 295 | "lav": "Latvian", 296 | "lim": "Limburgan; Limburger; Limburgish", 297 | "lin": "Lingala", 298 | "lit": "Lithuanian", 299 | "ltz": "Luxembourgish; Letzeburgesch", 300 | "lub": "Luba-Katanga", 301 | "lug": "Ganda", 302 | "mac": "Macedonian", 303 | "mah": "Marshallese", 304 | "mal": "Malayalam", 305 | "mao": "Maori", 306 | "mar": "Marathi", 307 | "may": "Malay", 308 | "mkd": "Macedonian", 309 | "mlg": "Malagasy", 310 | "mlt": "Maltese", 311 | "mon": "Mongolian", 312 | "mri": "Maori", 313 | "msa": "Malay", 314 | "mya": "Burmese", 315 | "nau": "Nauru", 316 | "nav": "Navajo; Navaho", 317 | "nbl": "Ndebele, South; South Ndebele", 318 | "nde": "Ndebele, North; North Ndebele", 319 | "ndo": "Ndonga", 320 | "nep": "Nepali", 321 | "nld": "Dutch; Flemish", 322 | "nno": "Norwegian Nynorsk; Nynorsk, Norwegian", 323 | "nob": "Bokmål, Norwegian; Norwegian Bokmål", 324 | "nor": "Norwegian", 325 | "nya": "Chichewa; Chewa; Nyanja", 326 | "oci": "Occitan (post 1500); Provençal", 327 | "oji": "Ojibwa", 328 | "ori": "Oriya", 329 | "orm": "Oromo", 330 | "oss": "Ossetian; Ossetic", 331 | "pan": "Panjabi; Punjabi", 332 | "per": "Persian", 333 | "pli": "Pali", 334 | "pol": "Polish", 335 | "por": "Portuguese", 336 | "pus": "Pushto; Pashto", 337 | "que": "Quechua", 338 | "roh": "Romansh", 339 | "ron": "Romanian; Moldavian; Moldovan", 340 | "rum": "Romanian; Moldavian; Moldovan", 341 | "run": "Rundi", 342 | "rus": "Russian", 343 | "sag": "Sango", 344 | "san": "Sanskrit", 345 | "sin": "Sinhala; Sinhalese", 346 | "slo": "Slovak", 347 | "slk": "Slovak", 348 | "slv": "Slovenian", 349 | "sme": "Northern Sami", 350 | "smo": "Samoan", 351 | "sna": "Shona", 352 | "snd": "Sindhi", 353 | "som": "Somali", 354 | "sot": "Sotho, Southern", 355 | "spa": "Spanish; Castilian", 356 | "sqi": "Albanian", 357 | "srd": "Sardinian", 358 | "srp": "Serbian", 359 | "ssw": "Swati", 360 | "sun": "Sundanese", 361 | "swa": "Swahili", 362 | "swe": "Swedish", 363 | "tah": "Tahitian", 364 | "tam": "Tamil", 365 | "tat": "Tatar", 366 | "tel": "Telugu", 367 | "tgk": "Tajik", 368 | "tgl": "Tagalog", 369 | "tha": "Thai", 370 | "tib": "Tibetan", 371 | "tir": "Tigrinya", 372 | "ton": "Tonga (Tonga Islands)", 373 | "tsn": "Tswana", 374 | "tso": "Tsonga", 375 | "tuk": "Turkmen", 376 | "tur": "Turkish", 377 | "twi": "Twi", 378 | "uig": "Uighur; Uyghur", 379 | "ukr": "Ukrainian", 380 | "urd": "Urdu", 381 | "uzb": "Uzbek", 382 | "ven": "Venda", 383 | "vie": "Vietnamese", 384 | "vol": "Volapük", 385 | "wel": "Welsh", 386 | "wln": "Walloon", 387 | "wol": "Wolof", 388 | "xho": "Xhosa", 389 | "yid": "Yiddish", 390 | "yor": "Yoruba", 391 | "zha": "Zhuang; Chuang", 392 | "zho": "Chinese", 393 | "zul": "Zulu" 394 | }, 395 | "full": [ 396 | "Afar", 397 | "Abkhazian", 398 | "Avestan", 399 | "Afrikaans", 400 | "Akan", 401 | "Amharic", 402 | "Aragonese", 403 | "Arabic", 404 | "Assamese", 405 | "Avaric", 406 | "Aymara", 407 | "Azerbaijani", 408 | "Bashkir", 409 | "Belarusian", 410 | "Bulgarian", 411 | "Bihari languages", 412 | "Bislama", 413 | "Bambara", 414 | "Bengali", 415 | "Tibetan", 416 | "Breton", 417 | "Bosnian", 418 | "Catalan; Valencian", 419 | "Chechen", 420 | "Chamorro", 421 | "Corsican", 422 | "Cree", 423 | "Czech", 424 | "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic", 425 | "Chuvash", 426 | "Welsh", 427 | "Danish", 428 | "German", 429 | "Divehi; Dhivehi; Maldivian", 430 | "Dzongkha", 431 | "Ewe", 432 | "Greek: Modern (1453-)", 433 | "English", 434 | "Esperanto", 435 | "Spanish; Castilian", 436 | "Estonian", 437 | "Basque", 438 | "Persian", 439 | "Fulah", 440 | "Finnish", 441 | "Fijian", 442 | "Faroese", 443 | "French", 444 | "Western Frisian", 445 | "Irish", 446 | "Gaelic; Scottish Gaelic", 447 | "Galician", 448 | "Guarani", 449 | "Gujarati", 450 | "Manx", 451 | "Hausa", 452 | "Hebrew", 453 | "Hindi", 454 | "Hiri Motu", 455 | "Croatian", 456 | "Haitian; Haitian Creole", 457 | "Hungarian", 458 | "Armenian", 459 | "Herero", 460 | "Interlingua (International Auxiliary Language Association)", 461 | "Indonesian", 462 | "Interlingue; Occidental", 463 | "Igbo", 464 | "Sichuan Yi; Nuosu", 465 | "Inupiaq", 466 | "Ido", 467 | "Icelandic", 468 | "Italian", 469 | "Inuktitut", 470 | "Japanese", 471 | "Javanese", 472 | "Georgian", 473 | "Kongo", 474 | "Kikuyu; Gikuyu", 475 | "Kuanyama; Kwanyama", 476 | "Kazakh", 477 | "Kalaallisut; Greenlandic", 478 | "Central Khmer", 479 | "Kannada", 480 | "Korean", 481 | "Kanuri", 482 | "Kashmiri", 483 | "Kurdish", 484 | "Komi", 485 | "Cornish", 486 | "Kirghiz; Kyrgyz", 487 | "Latin", 488 | "Luxembourgish; Letzeburgesch", 489 | "Ganda", 490 | "Limburgan; Limburger; Limburgish", 491 | "Lingala", 492 | "Lao", 493 | "Lithuanian", 494 | "Luba-Katanga", 495 | "Latvian", 496 | "Malagasy", 497 | "Marshallese", 498 | "Maori", 499 | "Macedonian", 500 | "Malayalam", 501 | "Mongolian", 502 | "Marathi", 503 | "Malay", 504 | "Maltese", 505 | "Burmese", 506 | "Nauru", 507 | "Bokmål: Norwegian; Norwegian Bokmål", 508 | "Ndebele: North; North Ndebele", 509 | "Nepali", 510 | "Ndonga", 511 | "Dutch; Flemish", 512 | "Norwegian Nynorsk; Nynorsk: Norwegian", 513 | "Norwegian", 514 | "Ndebele: South; South Ndebele", 515 | "Navajo; Navaho", 516 | "Chichewa; Chewa; Nyanja", 517 | "Occitan (post 1500); Provençal", 518 | "Ojibwa", 519 | "Oromo", 520 | "Oriya", 521 | "Ossetian; Ossetic", 522 | "Panjabi; Punjabi", 523 | "Pali", 524 | "Polish", 525 | "Pushto; Pashto", 526 | "Portuguese", 527 | "Quechua", 528 | "Romansh", 529 | "Rundi", 530 | "Romanian; Moldavian; Moldovan", 531 | "Russian", 532 | "Kinyarwanda", 533 | "Sanskrit", 534 | "Sardinian", 535 | "Sindhi", 536 | "Northern Sami", 537 | "Sango", 538 | "Sinhala; Sinhalese", 539 | "Slovak", 540 | "Slovenian", 541 | "Samoan", 542 | "Shona", 543 | "Somali", 544 | "Albanian", 545 | "Serbian", 546 | "Swati", 547 | "Sotho: Southern", 548 | "Sundanese", 549 | "Swedish", 550 | "Swahili", 551 | "Tamil", 552 | "Telugu", 553 | "Tajik", 554 | "Thai", 555 | "Tigrinya", 556 | "Turkmen", 557 | "Tagalog", 558 | "Tswana", 559 | "Tonga (Tonga Islands)", 560 | "Turkish", 561 | "Tsonga", 562 | "Tatar", 563 | "Twi", 564 | "Tahitian", 565 | "Uighur; Uyghur", 566 | "Ukrainian", 567 | "Urdu", 568 | "Uzbek", 569 | "Venda", 570 | "Vietnamese", 571 | "Volapük", 572 | "Walloon", 573 | "Wolof", 574 | "Xhosa", 575 | "Yiddish", 576 | "Yoruba", 577 | "Zhuang; Chuang", 578 | "Chinese", 579 | "Zulu", 580 | "Unknown" 581 | ] 582 | } 583 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | const spawn = require('child_process').spawn; 2 | const os = require('os'); 3 | const Path = require('path'); 4 | const fs = require('fs'); 5 | 6 | const consoleLogger = require('./consoleLogger.js'); 7 | const languages = require('./languages.json'); 8 | const packageSettings = require(Path.resolve(__dirname, '../package.json')); 9 | 10 | const moment = require("moment"); 11 | const hasbin = require('hasbin'); 12 | const colors = require('colors'); 13 | const _ = require('lodash'); 14 | const mime = require('mime'); 15 | const recursive = require('recursive-readdir'); 16 | const yargs = require('yargs'); 17 | const optional = require('optional'); 18 | 19 | const userSettings = optional("./settings.json") || {}; 20 | 21 | var exports = module.exports = { 22 | getStreamTitle: getStreamTitle, 23 | normalizeStreamLanguage: normalizeStreamLanguage, 24 | normalizeLanguage: normalizeLanguage, 25 | getFormatedChannels: getFormatedChannels, 26 | momentizeTimemark: momentizeTimemark, 27 | extractTrack: extractTrack, 28 | vobsubToSRT: vobsubToSRT, 29 | createListString: createListString, 30 | parseInput: parseInput, 31 | initStatsFile: initStatsFile, 32 | appendToStatsFile: appendToStatsFile, 33 | getCLIArguments: getCLIArguments, 34 | removeFromObject: removeFromObject, 35 | isSupportedFileType: isSupportedFileType 36 | }; 37 | 38 | String.prototype.capitalize = function() { 39 | return this.charAt(0).toUpperCase() + this.slice(1); 40 | }; 41 | 42 | function getStreamTitle(stream) { 43 | return stream.title || stream.tags ? stream.tags.title : undefined; 44 | } 45 | 46 | function normalizeStreamLanguage(stream) { 47 | let lang = stream.language || stream.tags ? stream.tags.language : undefined; 48 | return normalizeLanguage(lang); 49 | } 50 | 51 | function normalizeLanguage(lang) { 52 | if (typeof lang === 'undefined') 53 | return 'Unknown'; 54 | 55 | switch (lang.length) { 56 | case 2: 57 | return languages.alpha2Languages[lang] || "Unknown"; 58 | case 3: 59 | return languages.alpha3Languages[lang] || "Unknown"; 60 | default: 61 | return lang.capitalize() || "Unknown"; 62 | } 63 | } 64 | 65 | function getFormatedChannels(channels) { 66 | if (channels === 1) { 67 | return 'Mono'; 68 | } else if (channels === 2) { 69 | return 'Stereo'; 70 | } else if (channels % 2) { 71 | return channels + '.0 Channel'; 72 | } else { 73 | return (channels - 1) + '.1 Channel'; 74 | } 75 | } 76 | 77 | function momentizeTimemark(timemark) { 78 | 79 | let hours = parseInt(timemark.substring(0, timemark.indexOf(':')), 10); 80 | let minutes = parseInt(timemark.substring(timemark.indexOf(':') + 1, timemark.lastIndexOf(':')), 10); 81 | let seconds = parseFloat(timemark.substr(timemark.lastIndexOf(':') + 1)); 82 | 83 | return moment.duration().add(hours, 'h').add(minutes, 'm').add(seconds, 's'); 84 | } 85 | 86 | function extractTrack(videoPath, trackIndex) { 87 | return new Promise(function(resolve, reject) { 88 | if (!hasbin.sync('mkvextract')) { 89 | return reject(new Error("MKVEXTRACT_NOT_INSTALLED")); 90 | } 91 | 92 | let base = Path.basename(videoPath); 93 | let output = Path.join(Path.resolve(os.tmpdir(), packageSettings.name), 'TRACK' + trackIndex + '_' + base.replace(/\.[^/.]+$/, "")); 94 | 95 | let process = spawn('mkvextract', ['tracks', videoPath, trackIndex + ':' + output]) 96 | .on('close', (code) => { 97 | if (code !== 0) { 98 | return reject(new Error('MKVEXTRACT_ERROR ' + code)); 99 | } 100 | resolve(output); 101 | }); 102 | process.stdout.on('data', function(data) { 103 | outputHandler('mkvextract', data); 104 | }); 105 | process.stderr.on('data', function(data) { 106 | outputHandler('mkvextract', data); 107 | }); 108 | 109 | }); 110 | } 111 | 112 | function outputHandler(tool, data) { 113 | // logger.debug(colors.bgMagenta.white('[' + tool + ']'), data.toString('utf8'), { 114 | // __clearLine: true 115 | // }); 116 | } 117 | 118 | 119 | function vobsubToSRT(filePath) { 120 | return new Promise(function(resolve, reject) { 121 | if (!hasbin.sync('vobsub2srt')) { 122 | return reject(new Error('VOBSUB2SRT_NOT_INSTALLED')); 123 | } 124 | let filePathWithoutExtension = filePath.replace(/\.[^/.]+$/, ""); 125 | 126 | let process = spawn('vobsub2srt', [filePathWithoutExtension]) 127 | .on('close', (code) => { 128 | if (code !== 0) { 129 | return reject(new Error('VOBSUB2SRT_ERROR')); 130 | } 131 | resolve(filePathWithoutExtension + '.srt'); 132 | }); 133 | process.stdout.on('data', function(data) { 134 | outputHandler('vobsub2srt', data); 135 | }); 136 | process.stderr.on('data', function(data) { 137 | outputHandler('vobsub2srt', data); 138 | }); 139 | }); 140 | } 141 | 142 | function createListString(files) { 143 | if (Array.isArray(files)) 144 | return '\n\t- ' + files.join('\n\t- '); 145 | 146 | let array = []; 147 | _.each(files, function(value, key) { 148 | array.push(colors.yellow(key) + ': ' + value); 149 | }); 150 | return '\n\t- ' + array.join('\n\t- '); 151 | } 152 | 153 | function parseInput(input, logger) { 154 | logger = logger ? logger : consoleLogger; 155 | return new Promise(function(resolve, reject) { 156 | let fileDescriptorStats; 157 | try { 158 | fileDescriptorStats = fs.lstatSync(input); 159 | } catch (e) { 160 | if (e.code === 'ENOENT') { 161 | logger.error('Input', input, 'does not exist.'); 162 | resolve([]); 163 | } 164 | throw e; 165 | } 166 | 167 | // Check if input is a file 168 | if (fileDescriptorStats.isFile()) { 169 | if (isSupportedFileType(input)) { 170 | resolve([input]); 171 | } else 172 | return reject('Input file \'' + input + '\' is not a recognized file format.'); 173 | } 174 | 175 | // Check if input is a directory 176 | else if (fileDescriptorStats.isDirectory()) { 177 | // Get all files in directory 178 | findVideos(input).then(function(videoPaths) { 179 | logger.verbose('Folder encoding started at', colors.yellow(moment().format("dddd, MMMM Do YYYY, h:mm:ss A"))); 180 | resolve(videoPaths); 181 | }).catch(function(err) { 182 | reject(err); 183 | }); 184 | } 185 | 186 | }); 187 | } 188 | 189 | function findVideos(path) { 190 | return new Promise(function(resolve, reject) { 191 | recursive(path, function(err, files) { 192 | 193 | let videos = []; 194 | 195 | // Handle any errors given while searching input directory 196 | if (err) { 197 | if (err.code === 'ENOENT') 198 | return reject(new Error('File or directory ' + colors.yellow(path) + ' does not exist.')); 199 | else 200 | throw err; 201 | } 202 | 203 | 204 | // Check if each file is a video 205 | _.each(files, function(file) { 206 | if (isSupportedFileType(file)) { 207 | videos.push(file); 208 | } 209 | }); 210 | 211 | resolve(videos); 212 | }); 213 | }); 214 | } 215 | 216 | function isSupportedFileType(file) { 217 | if (mime.lookup(file).startsWith('video/') || Path.extname(file) === '.m2ts') 218 | return true; 219 | else 220 | return false; 221 | } 222 | 223 | function initStatsFile(path) { 224 | return new Promise(function(resolve, reject) { 225 | loadStatsFile(path).then(function(stream) { 226 | resolve(stream); 227 | }).catch(reject); 228 | }); 229 | } 230 | 231 | function loadStatsFile(path) { 232 | return new Promise(function(resolve, reject) { 233 | fs.access(path, fs.F_OK, function(err) { 234 | let stream = fs.createWriteStream(path, { 235 | 'flags': 'a' 236 | }); 237 | 238 | if (err) { 239 | stream.write('Encoded Date,Relative Path,Original Size,New Size,Percentage,Duration of Encode'); 240 | } 241 | 242 | resolve(stream); 243 | }); 244 | }); 245 | } 246 | 247 | function appendToStatsFile(data, statsFile) { 248 | return new Promise(function(resolve, reject) { 249 | _.each(data, function(val, i) { 250 | if (val.indexOf(',') > -1) { 251 | data[i] = '"' + val + '"'; 252 | } 253 | }); 254 | statsFile.write('\n' + data.join(','), 'utf-8', function(err) { 255 | if (err) 256 | return reject(err); 257 | resolve(); 258 | }); 259 | }); 260 | } 261 | 262 | function getCLIArguments() { 263 | return yargs 264 | .usage(colors.underline('Usage:') + ' $0 [options] file|directory') 265 | .options({ 266 | 'd': { 267 | alias: 'destination', 268 | default: userSettings['destination'] || Path.resolve(process.cwd(), 'h265'), 269 | describe: 'Folder where encoded files are output.', 270 | type: 'string', 271 | normalize: true, 272 | group: 'General:' 273 | }, 274 | // 'g': { 275 | // alias: 'temp-directory', 276 | // default: userSettings['temp-directory'] || Path.resolve(os.tmpdir(), packageSettings.name), 277 | // describe: 'Folder where files are stored during encoding.', 278 | // type: 'string', 279 | // normalize: true, 280 | // group: 'General:' 281 | // }, 282 | // 'log-file': { 283 | // default: userSettings['log-file'] || Path.resolve(process.cwd(), 'h265ize.log'), 284 | // describe: 'Sets the log file location for all output from h265ize. Enable debug mode via the --debug flag to output to the log file.', 285 | // type: 'string', 286 | // normalize: true, 287 | // group: 'General:' 288 | // }, 289 | 'm': { 290 | alias: 'preset', 291 | default: userSettings['preset'] || 'fast', 292 | describe: 'x265 encoder preset.', 293 | choices: ['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow', 'placebo'], 294 | type: 'string', 295 | group: 'General:' 296 | }, 297 | 'as-preset': { 298 | default: userSettings['as-preset'] || 'none', 299 | describe: 'My personal presets. Descriptions of each preset\'s use and function can be found on the github page.', 300 | choices: ['anime-high', 'anime-medium', 'testing-ssim', 'none'], 301 | type: 'string', 302 | group: 'Video:' 303 | }, 304 | 'n': { 305 | alias: 'native-language', 306 | default: userSettings['native-language'] || '', 307 | describe: 'The native language used to select default audio and subtitles. You may use 3 letter or 2 letter ISO 639-2 Alpha-3/Alpha-2 codes or the full language name. Examples: [eng|en|English|jpn|ja|Japanese]', 308 | type: 'string', 309 | group: 'General:' 310 | }, 311 | 'f': { 312 | alias: 'output-format', 313 | default: userSettings['output-format'] || 'mkv', 314 | describe: 'Output container format.', 315 | choices: ['mkv', 'mp4', 'm4v'], 316 | type: 'string', 317 | group: 'General:' 318 | }, 319 | 'x': { 320 | alias: 'extra-options', 321 | default: userSettings['extra-options'] || '', 322 | describe: 'Extra x265 options. Options can be found on the x265 options page.', 323 | type: 'string', 324 | group: 'Video:' 325 | }, 326 | 'q': { 327 | alias: 'quality', 328 | default: userSettings['quality'] || 19, 329 | describe: 'Sets the qp quality target', 330 | type: 'number', 331 | group: 'General:' 332 | }, 333 | 'video-bitrate': { 334 | default: userSettings['video-bitrate'] || 0, 335 | describe: 'Sets the video bitrate, set to 0 to use qp rate control instead of a target bitrate.', 336 | type: 'number', 337 | group: 'Video:' 338 | }, 339 | 'l': { 340 | alias: 'preview-length', 341 | default: userSettings['preview-length'] || 30000, 342 | describe: 'Milliseconds to encode in preview mode. Max is half the length of input video.', 343 | type: 'number', 344 | group: 'Advanced:' 345 | }, 346 | // 'time-drift-limit': { 347 | // default: userSettings['time-drift-limit'] || 200, 348 | // describe: 'Milliseconds the finished encode is allowed to differ from the original\'s length.', 349 | // type: 'number', 350 | // group: 'Advanced:' 351 | // }, 352 | 'accurate-timestamps': { 353 | default: userSettings['accurate-timestamps'] || false, 354 | describe: 'Become blu-ray complient and reduce the max keyInt to the average frame rate.', 355 | type: 'boolean', 356 | group: 'Video:' 357 | }, 358 | 'he-audio': { 359 | default: userSettings['he-audio'] || false, 360 | describe: 'Re-encode audio to opus at 40kbps/channel.', 361 | type: 'boolean', 362 | group: 'Audio:' 363 | }, 364 | 'force-he-audio': { 365 | default: userSettings['force-he-audio'] || false, 366 | describe: 'Convert all audio to HE format, including lossless formats.', 367 | type: 'boolean', 368 | group: 'Audio:' 369 | }, 370 | 'downmix-he-audio': { 371 | default: userSettings['downmix-he-audio'] || false, 372 | describe: 'Downmix he-audio opus to Dolby Pro Logic II at 40 kbps/channel. Enables he-audio.', 373 | type: 'boolean', 374 | group: 'Audio:' 375 | }, 376 | 'o': { 377 | alias: 'override', 378 | default: userSettings['override'] || false, 379 | describe: 'Enable override mode. Allows conversion of videos that are already encoded by the hevc codec.', 380 | type: 'boolean', 381 | group: 'General:' 382 | }, 383 | 'p': { 384 | alias: 'preview', 385 | default: userSettings['preview'] || false, 386 | describe: 'Only encode a preview of the video starting at middle of video. See -l/--preview-length for more info.', 387 | type: 'boolean', 388 | group: 'General:' 389 | }, 390 | 'multi-pass': { 391 | default: userSettings['mutli-pass'] || 0, 392 | describe: 'Enable multiple passes by the encoder. Must be greater than 1.', 393 | type: 'number', 394 | group: 'Video:' 395 | }, 396 | 'stats': { 397 | default: userSettings['stats'] || false, 398 | describe: 'Output a stats file containing stats for each video converted.', 399 | type: 'boolean', 400 | group: 'Advanced:' 401 | }, 402 | 'v': { 403 | alias: 'verbose', 404 | default: userSettings['verbose'] || false, 405 | describe: 'Enables verbose mode. Prints extra information.', 406 | type: 'boolean', 407 | group: 'General:' 408 | }, 409 | 'watch': { 410 | default: userSettings['watch'] || '', 411 | describe: 'Watches a directory for new video files to be converted.', 412 | type: 'string', 413 | group: 'Advanced:' 414 | }, 415 | 'bitdepth': { 416 | default: userSettings['bitdepth'] || 0, 417 | describe: 'Forces encoding videos at a specific bitdepth. Set to 0 to maintain original bitdepth.', 418 | type: 'number', 419 | group: 'Video:' 420 | }, 421 | 'screenshots': { 422 | default: userSettings['screenshots'] || false, 423 | describe: 'Take 6 screenshots at regular intervals throughout the finished encode.', 424 | type: 'boolean', 425 | group: 'Video:' 426 | }, 427 | 'normalize-level': { 428 | default: userSettings['normalize-level'] || 2, 429 | describe: 'Level of normalization to be applied. See https://github.com/FallingSnow/h265ize/issues/56 for more info.', 430 | type: 'number', 431 | group: 'Advanced:' 432 | }, 433 | 'scale': { 434 | default: userSettings['scale'] || false, 435 | describe: 'Width videos should be scaled to. Videos will always maintain original aspect ratio. [Examples: 720, 480]', 436 | type: 'number', 437 | group: 'Video:' 438 | }, 439 | 'debug': { 440 | default: userSettings['debug'] || false, 441 | describe: 'Enables debug mode. Prints extra debugging information.', 442 | type: 'boolean', 443 | group: 'Advanced:' 444 | }, 445 | 'delete': { 446 | default: userSettings['delete'] || false, 447 | describe: 'Delete source after encoding is complete and replaces it with new encode. [DANGER]', 448 | type: 'boolean', 449 | group: 'Advanced:' 450 | }, 451 | 'help': { 452 | describe: 'Displays help page.', 453 | group: 'Options:' 454 | }, 455 | 'test': { 456 | default: userSettings['test'] || false, 457 | describe: 'Puts h265ize in test mode. No files will be encoded.', 458 | type: 'boolean', 459 | group: 'Advanced:' 460 | }, 461 | 'version': { 462 | describe: 'Displays version information.', 463 | group: 'Options:' 464 | }, 465 | }) 466 | .argv; 467 | } 468 | 469 | function removeFromObject(obj, key) { 470 | if (typeof obj[key] !== 'undefined') { 471 | delete obj[key]; 472 | obj.length -= 1; 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /lib/classes/video.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const os = require('os'); 3 | const EventEmitter = require('events'); 4 | 5 | const _ = require('lodash'); 6 | const colors = require('colors'); 7 | const moment = require('moment'); 8 | require('moment-duration-format'); 9 | const filesize = require('filesize'); 10 | const fs = require('fs-extra'); 11 | const math = require('mathjs'); 12 | const Promise = require('bluebird'); 13 | const Pauser = require('promise-pauser'); 14 | const ffmpeg = require('fluent-ffmpeg'); 15 | const MemoryStream = require('memorystream'); 16 | 17 | const helpers = require(Path.join(__dirname, '../helpers.js')); 18 | const ASPresets = require('../aspresets.json'); 19 | 20 | let counter = 0; 21 | 22 | class Video { 23 | constructor(path, options) { 24 | 25 | // Check if file exists 26 | fs.accessSync(path, fs.F_OK); 27 | 28 | this.encoder = null; 29 | this.id = counter++; 30 | 31 | // Path related data 32 | this.path = path; 33 | let pathParsed = Path.parse(this.path); 34 | this.base = pathParsed.base; 35 | this.dir = pathParsed.dir; 36 | this.ext = pathParsed.ext; 37 | this.name = pathParsed.name; 38 | this.root = pathParsed.root; 39 | 40 | // Setup encoding options 41 | this.options = {}; 42 | _.defaults(this.options, options, { 43 | preview: false, 44 | quality: 19, 45 | override: false, 46 | stats: false, 47 | HEAudioBitrate: 40, 48 | destination: os.homedir() 49 | }); 50 | this.output = { 51 | base: this.name + (this.options.preview ? '-preview' : '') + '.' + this.options.outputFormat, 52 | dir: this.options.destination, 53 | sample: this.name + '-sample.' + this.options.outputFormat 54 | }; 55 | this.output.path = Path.join(this.output.dir, this.output.base); 56 | 57 | this.running = false; 58 | this.paused = false; 59 | this.status; 60 | 61 | this.ffmpegCommand = new ffmpeg().input(this.path).renice(10) 62 | .videoCodec('libx265').audioCodec('copy').outputOptions('-c:s', 'copy') 63 | .outputOptions('-c:d', 'copy'); 64 | this.promiseChain; 65 | this.pauser = Pauser.pauser(); 66 | this.inputCounter = 0; 67 | this.pass = 0; 68 | this.x265Options = ''; 69 | this.previewStream = new MemoryStream(); 70 | this.temp = { 71 | files: [] 72 | }; 73 | 74 | this.events = new EventEmitter(); 75 | 76 | this.currentStageNum = -1; 77 | this.currentStage = { 78 | name: 'Pending', 79 | action: 'pending' 80 | }; 81 | this.stages = [{ 82 | name: 'Initialize filesystem', 83 | action: 'initializing filesystem', 84 | promise: this.filesystem 85 | }, { 86 | name: 'Get Initial Metadata', 87 | action: 'getting intial metadata', 88 | promise: this.getInitialMetadata 89 | }, { 90 | name: 'Process Streams', 91 | action: 'processing streams', 92 | promise: this.processStreams 93 | }, { 94 | name: 'Set AS Preset', 95 | action: 'setting as preset', 96 | promise: this.setASPreset 97 | }, { 98 | name: 'Upconvert', 99 | action: 'upconverting', 100 | promise: this.upconvert 101 | }, { 102 | name: 'Set Video Bit Depth', 103 | action: 'detecting video bit depth', 104 | promise: this.setVideoBitDepth 105 | }, { 106 | name: 'Normalize Audio', 107 | action: 'normalizing audio', 108 | promise: this.normalizeAudio 109 | }, { 110 | name: 'Auto Crop', 111 | action: 'detecting crop', 112 | promise: this.autoCrop 113 | }, { 114 | name: 'Deinterlace', 115 | action: 'deinterlacing', 116 | promise: this.deinterlace 117 | }, { 118 | name: 'Map Streams', 119 | action: 'mapping streams', 120 | promise: this.mapStreams 121 | }, { 122 | name: 'Map High Efficiency Audio', 123 | action: 'mapping high effeciency audio', 124 | promise: this.heAudio 125 | }, { 126 | name: 'Encode', 127 | action: 'encoding', 128 | promise: this.encode 129 | }, { 130 | name: 'Multipass', 131 | action: 'generating multipass', 132 | promise: this.multiPass 133 | }, { 134 | name: 'Verify Encode', 135 | action: 'verifying encode', 136 | promise: this.verifyEncode 137 | }, { 138 | name: 'Move Output', 139 | action: 'moving output', 140 | promise: this.move 141 | }, { 142 | name: 'Screenshots', 143 | action: 'creating screenshots', 144 | promise: this.screenshots 145 | }, { 146 | name: 'Sample', 147 | action: 'creating sample', 148 | promise: this.sample 149 | }, { 150 | name: 'Stats', 151 | action: 'appending stats', 152 | promise: this.appendStats 153 | }]; 154 | } 155 | 156 | _addX265Option(option) { 157 | this.x265Options += this.x265Options.length ? ':' + option : option; 158 | } 159 | 160 | filesystem() { 161 | let _self = this; 162 | return new Promise(function(resolve, reject) { 163 | 164 | 165 | if (fs.existsSync(_self.output.path)) { 166 | return reject(new Error('Output ' + colors.yellow('"' + _self.output.path + '"') + ' already exists.')); 167 | } 168 | 169 | resolve(); 170 | 171 | }); 172 | } 173 | 174 | getInitialMetadata() { 175 | let _self = this; 176 | return new Promise(function(resolve, reject) { 177 | Video.getMetadata(_self).then(function(metadata) { 178 | _self.metadata = metadata; 179 | resolve(); 180 | }, reject); 181 | 182 | }); 183 | } 184 | 185 | processStreams() { 186 | let _self = this; 187 | return new Promise(function(resolve, reject) { 188 | 189 | let videoStreams = [], 190 | audioStreams = [], 191 | subtitleStreams = [], 192 | otherStreams = []; 193 | 194 | // Dissect each video stream 195 | _.each(_self.metadata.streams, function(stream) { 196 | _self.encoder.logger.debug('Working on stream:', stream.index); 197 | stream.input = 0; 198 | 199 | // this.encoder.logger.debug(stream); 200 | 201 | if (!stream.codec_type) { 202 | _self.encoder.logger.warn('A codec was not provided for stream ' + stream.index + '. Your ffmpeg is most likely out of date. At least version 2.8.2 is recommended.'); 203 | } 204 | 205 | switch (stream.codec_type) { 206 | case 'video': 207 | if (stream.codec_name === 'hevc' && !_self.options.override) 208 | return reject(new Error('Already encoded in h265. Skipping... (use the --override flag to encode hevc videos)')); 209 | videoStreams.push(stream); 210 | break; 211 | case 'audio': 212 | audioStreams.push(stream); 213 | break; 214 | case 'subtitle': 215 | subtitleStreams.push(stream); 216 | break; 217 | default: 218 | if (stream.codec_name === 'unknown') { 219 | _self.encoder.logger.warn('Codec stream with index ' + stream.index + ' will not be included because it has an unknown codec.'); 220 | break; 221 | } 222 | otherStreams.push(stream); 223 | break; 224 | } 225 | }); 226 | 227 | // Preview Mode 228 | if (_self.options.preview) { 229 | _self.ffmpegCommand.seekInput(_self.metadata.format.duration / 2).duration(_self.options.previewLength / 1000); 230 | } 231 | 232 | if (_self.options.multiPass > 1) { 233 | _self._addX265Option('pass=1:stats=' + Path.join(os.tmpdir(), 'x265stats.log')); 234 | } 235 | 236 | _self.streams = { 237 | videoStreams: videoStreams, 238 | audioStreams: audioStreams, 239 | subtitleStreams: subtitleStreams, 240 | otherStreams: otherStreams 241 | }; 242 | resolve(); 243 | }); 244 | } 245 | 246 | setASPreset() { 247 | let _self = this; 248 | return new Promise(function(resolve, reject) { 249 | if (!_self.options.asPreset) 250 | return resolve(); 251 | 252 | if (_self.options.asPreset === 'none') 253 | return resolve(); 254 | 255 | for (let preset of _self.options.asPreset.split(':')) { 256 | let ASPreset = ASPresets[preset]; 257 | if (!ASPreset) 258 | return reject(new Error('Unknown as-preset option ' + colors.yellow(preset) + '.')); 259 | 260 | for (let option in ASPreset) { 261 | switch (option) { 262 | case 'x265Options': 263 | _self._addX265Option(ASPreset[option]); 264 | break; 265 | case 'preset': 266 | _self.options.preset = ASPreset[option]; 267 | break; 268 | case 'videoFilters': 269 | _self.ffmpegCommand.videoFilters(ASPreset[option]); 270 | break; 271 | case 'bitdepth': 272 | _self.options.bitdepth = ASPreset[option]; 273 | break; 274 | default: 275 | return reject(new Error('Unknown as-preset setting ' + colors.yellow(option) + ' for as-preset ' + colors.yellow(_self.options.asPreset) + '.')); 276 | } 277 | } 278 | } 279 | resolve(); 280 | }); 281 | } 282 | 283 | upconvert() { 284 | let _self = this; 285 | return new Promise(function(resolve, reject) { 286 | if (_self.options.upconvert || _self.options.test) 287 | return resolve(); 288 | 289 | 290 | let trackUpconvertProcesses = []; 291 | 292 | // Here you can process all subtitles 293 | _.each(_self.streams.subtitleStreams, function(subtitle, i) { 294 | 295 | // Detect dvdsub subtitles 296 | if (subtitle.codec_name === 'dvdsub' || subtitle.codec_name === 'dvd_subtitle') { 297 | 298 | // Convert dvdsub subtitle to srt 299 | trackUpconvertProcesses.push(new Promise(function(resolve, reject) { 300 | helpers.extractTrack(_self.path, subtitle.index).then(helpers.vobsubToSRT).then(function(filePath) { 301 | _self.ffmpegCommand.input(filePath); 302 | _self.temp.files.push(filePath); 303 | Video.getMetadata(_self).then(function(metadata) { 304 | metadata.streams[0].title = subtitle.title; 305 | metadata.streams[0].language = subtitle.language; 306 | metadata.streams[0].tags = subtitle.tags; 307 | metadata.streams[0].disposition = subtitle.disposition; 308 | metadata.streams[0].input = ++_self.inputCounter; 309 | 310 | _self.streams.subtitleStreams[i] = metadata.streams[0]; 311 | resolve(); 312 | }, reject); 313 | }).catch(reject); 314 | })); 315 | } 316 | }); 317 | 318 | _self.encoder.logger.debug('Upconverting', trackUpconvertProcesses.length, 'tracks.'); 319 | // Execute all upconvert processes 320 | Promise.all(trackUpconvertProcesses).then(function() { 321 | resolve(); 322 | }).catch(function(err) { 323 | _self.encoder.logger.warn('Upconvert error: ' + err.message + ' - Skipping upconvert...'); 324 | reject(err); 325 | }); 326 | 327 | resolve(); 328 | }); 329 | } 330 | 331 | setVideoBitDepth() { 332 | let _self = this; 333 | return new Promise(function(resolve, reject) { 334 | 335 | // Video streams 336 | let videoIndex = 0, 337 | videoBitDepth = 8; 338 | 339 | if (_self.streams.videoStreams.length > 1) { 340 | // TODO implement feature 341 | _self.encoder.logger.alert('More than one video stream detected. Using the video stream with the greatest duration.'); 342 | videoIndex = 0; 343 | } 344 | let videoStream = _self.videoStream = _self.streams.videoStreams[videoIndex]; 345 | 346 | // Check for 12bit or 10bit video 347 | if (videoStream.pix_fmt.indexOf('12le') > -1 || videoStream.pix_fmt.indexOf('12be') > -1) { 348 | videoBitDepth = 12; 349 | } else if (videoStream.pix_fmt.indexOf('10le') > -1 || videoStream.pix_fmt.indexOf('10be') > -1) { 350 | videoBitDepth = 10; 351 | } 352 | _self.videoBitDepth = videoBitDepth; 353 | 354 | // Set video encoding profile 355 | if (_self.options.bitdepth === 12) { 356 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p12le'); 357 | } else if (_self.options.bitdepth === 10) { 358 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p10le'); 359 | } else if (_self.options.bitdepth === 8) { 360 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p'); 361 | } else { 362 | switch (_self.videoBitDepth) { 363 | case 16: 364 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p16le'); 365 | break; 366 | case 14: 367 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p14le'); 368 | break; 369 | case 12: 370 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p12le'); 371 | break; 372 | case 10: 373 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p10le'); 374 | break; 375 | default: 376 | _self.ffmpegCommand.outputOptions('-pix_fmt', 'yuv420p'); 377 | break; 378 | } 379 | } 380 | 381 | // Make sure we are only attempting to use 8 bit with fallback 382 | // binary 383 | // TODO 384 | // if (usingFallbackFfmpeg) { 385 | // let options = command._currentOutput.options.get(); 386 | // let selectedPixFmt = options[options.indexOf('-pix_fmt') + 1]; 387 | // if (selectedPixFmt !== 'yuv420p') { 388 | // return reject({ 389 | // level: 'error', 390 | // message: 'Bit depth about 8 bit are not supported by the fallback ffmpeg library. Try installing ffmpeg.' 391 | // }); 392 | // } 393 | // } 394 | 395 | resolve(); 396 | }); 397 | } 398 | 399 | normalizeAudio() { 400 | let _self = this; 401 | return new Promise(function(resolve, reject) { 402 | 403 | if (!_self.options.normalizeLevel || _self.options.normalizeLevel < 3) { 404 | return resolve(); 405 | } 406 | 407 | let availableFilters; 408 | 409 | ffmpeg.getAvailableFilters(function(err, filters) { 410 | availableFilters = filters; 411 | normalize(); 412 | }); 413 | 414 | function normalize() { 415 | 416 | _self.stages[_self.currentStageNum].command = new ffmpeg(_self.path, { 417 | logger: _self.encoder.logger 418 | }).inputOptions('-hide_banner').format('null').output('-') 419 | .on('start', function(commandLine) { 420 | if (_self.paused) 421 | _self.pause(); 422 | _self.encoder.logger.debug('Running Query:', commandLine); 423 | }) 424 | .on('error', function(err, stdout, stderr) { 425 | _self.encoder.logger.debug(err.stack); 426 | 427 | _self.encoder.logger.debug(stderr); 428 | 429 | if (err.message.startsWith('ffmpeg was killed with signal')) 430 | reject(new Error('ENDING')); 431 | else 432 | reject(err); 433 | }); 434 | let vdc = _self.stages[_self.currentStageNum].command; 435 | 436 | if (_self.options.preview) { 437 | vdc.seekInput(_self.metadata.format.duration / 2).duration(_self.options.previewLength / 1000); 438 | } 439 | 440 | // Only map streams we are going to work on 441 | _.each(_self.streams.audioStreams, function(stream, i) { 442 | vdc.outputOptions('-map', stream.input + ':' + stream.index); 443 | }); 444 | 445 | 446 | /************ This is where the normalization detection happens ***/ 447 | 448 | // 449 | if (_self.options.normalizeLevel >= 5) { 450 | _self.encoder.logger.debug('Normalizing audio via dynaudnorm.'); 451 | 452 | if (!availableFilters.dynaudnorm) { 453 | return reject(new Error('Installed ffmpeg version does not support the dynaudnorm audio filter')); 454 | } 455 | 456 | return reject(new Error('dynaudnorm is not currently implemented')); 457 | 458 | } 459 | 460 | // 461 | else if (_self.options.normalizeLevel >= 4) { 462 | _self.encoder.logger.debug('Normalizing audio via loudnorm.'); 463 | 464 | if (!availableFilters.loudnorm) { 465 | return reject(new Error('Installed ffmpeg version does not support the loudnorm audio filter')); 466 | } 467 | 468 | let parsedAttributes; 469 | 470 | // Oh loudnorm multipass, lets begin 471 | _.each(_self.streams.audioStreams, function(stream, i) { 472 | vdc.outputOptions('-filter_complex', '[' + stream.input + ':' + stream.index + ']loudnorm=I=-16:TP=-2.0:LRA=11:print_format=json'); 473 | }); 474 | 475 | vdc.on('end', function(stdout, stderr) { 476 | delete _self.stages[_self.currentStageNum].command; 477 | let unparsedStart = stderr.lastIndexOf('[Parsed_loudnorm') + 38; 478 | let unparsed = stderr.substr(unparsedStart).replace(/\r?\n|\r/g, ''); 479 | parsedAttributes = JSON.parse(unparsed); 480 | _self.ffmpegCommand.audioFilters({ 481 | filter: 'loudnorm', 482 | options: 'I=-16:TP=-2.0:LRA=11:measured_I=' + parsedAttributes.input_i + ':measured_LRA=' + parsedAttributes.input_lra + ':measured_TP=' + parsedAttributes.input_tp + ':measured_thresh=' + parsedAttributes.input_thresh + ':offset=' + parsedAttributes.target_offset + ':linear=true:print_format=summary' 483 | }); 484 | return resolve(); 485 | }); 486 | 487 | vdc.run(); 488 | } 489 | 490 | // "simple" audio RMS-based normalization to -2.0 dBFS 491 | else if (_self.options.normalizeLevel >= 3) { 492 | _self.encoder.logger.debug('Normalizing audio via volumedetect & volume.'); 493 | 494 | if (!availableFilters.volumedetect || !availableFilters.volume) { 495 | return reject(new Error('Installed ffmpeg version does not support volumedetect audio filter and/or volume audio filter')); 496 | } 497 | 498 | const volumeRegexp = /max_volume: (-?[0-9]+\.[0-9]+)/g; 499 | let volumeLevels = []; 500 | _.each(_self.streams.audioStreams, function(stream, i) { 501 | vdc.outputOptions('-filter_complex', '[' + stream.input + ':' + stream.index + ']volumedetect'); 502 | }); 503 | 504 | vdc.on('stderr', function(stderrLine) { 505 | if (stderrLine.startsWith('[Parsed_volumedetect')) { 506 | let match = volumeRegexp.exec(stderrLine); 507 | if (match) { 508 | volumeLevels.push(parseFloat(match[1])); 509 | } 510 | } 511 | // _self.encoder.logger.debug(colors.bgMagenta.white('[ffmpeg]'), stderrLine); 512 | }).on('end', function(stderr) { 513 | delete _self.stages[_self.currentStageNum].command; 514 | _.each(_self.streams.audioStreams, function(stream, i) { 515 | let volume = volumeLevels[i] * -1 - 2.0; 516 | _self.ffmpegCommand.outputOptions('-filter_complex', '[' + stream.input + ':' + stream.index + ']volume=' + volume + 'dB'); 517 | _self.ffmpegCommand.outputOptions('-c:' + stream.input + ':' + stream.index, 'aac'); 518 | // FIXME Hardcoded bitrate 519 | let bitratePerChannel = 128; 520 | _self.ffmpegCommand.outputOptions('-b:' + stream.input + ':' + stream.index, bitratePerChannel * stream.channels + 'k'); 521 | }); 522 | return resolve(); 523 | }); 524 | 525 | vdc.run(); 526 | } 527 | } 528 | 529 | }); 530 | } 531 | 532 | autoCrop() { 533 | let _self = this; 534 | return new Promise(function(resolve, reject) { 535 | if (_self.options.normalizeLevel < 1) { 536 | return resolve(); 537 | } 538 | 539 | const intervals = 12; 540 | const interval = _self.metadata.format.duration / (intervals + 1); 541 | 542 | function detectCrop(start, fallback) { 543 | return new Promise(function(resolve, reject) { 544 | const cropRegexp = /crop=(-?[0-9]+):(-?[0-9]+):(-?[0-9]+):(-?[0-9]+)/g; 545 | _self.stages[_self.currentStageNum].command = new ffmpeg(_self.path, { 546 | logger: _self.encoder.logger 547 | }).outputOptions('-map', _self.videoStream.input + ':' + _self.videoStream.index) 548 | .videoCodec('rawvideo').videoFilters("cropdetect=0.094:2:0").format('null').output('-'); 549 | 550 | if (fallback) { 551 | _self.encoder.logger.warn('Crop detection failed! Running crop detection in fallback mode. This is significantly slower.'); 552 | } else { 553 | _self.stages[_self.currentStageNum].command.frames(2).seekInput(start); 554 | } 555 | 556 | let crop = {}; 557 | 558 | _self.stages[_self.currentStageNum].command 559 | .on('start', function(commandLine) { 560 | if (_self.paused) 561 | _self.pause(); 562 | 563 | _self.encoder.logger.debug('Running Query:', commandLine); 564 | }) 565 | .on('end', function(stdout, stderr) { 566 | delete _self.stages[_self.currentStageNum].command; 567 | 568 | let match = cropRegexp.exec(stderr); 569 | if (match === null) { 570 | return reject(new Error('Could not run crop detection.')); 571 | } 572 | 573 | crop.w = parseInt(match[1], 10); 574 | crop.h = parseInt(match[2], 10); 575 | crop.x = parseInt(match[3], 10); 576 | crop.y = parseInt(match[4], 10); 577 | resolve(crop); 578 | }) 579 | // .on('stderr', function(line) { 580 | // this.encoder.logger.debug(line); 581 | // }) 582 | .on('error', function(err, stdout, stderr) { 583 | _self.encoder.logger.debug(err.stack); 584 | 585 | if (err.message.startsWith('ffmpeg was killed with signal')) 586 | return reject(new Error('ENDING')); 587 | else 588 | return reject(err); 589 | }); 590 | 591 | _self.stages[_self.currentStageNum].command.run(); 592 | }); 593 | } 594 | 595 | // 596 | let i = intervals, 597 | counter = 1, 598 | cropDetections = []; 599 | 600 | // This just runs all the ffmpeg crop detections in sync so you 601 | // dont end up with a million threads running 602 | function syncHandler(crop) { 603 | if (crop) 604 | cropDetections.push(crop); 605 | if (i > 0) { 606 | _self.encoder.logger.info('Crop Detection:', (counter++) + '/' + intervals, { 607 | __clearLine: true 608 | }); 609 | let startTime = interval * i--; 610 | detectCrop(startTime).then(syncHandler, function() { 611 | return detectCrop(startTime, true).then(function(crop) { 612 | cropDetections.push(crop); 613 | cropDetectionComplete(cropDetections); 614 | }, reject); 615 | }); 616 | } else 617 | cropDetectionComplete(cropDetections); 618 | } 619 | syncHandler(); 620 | 621 | 622 | 623 | // TODO: this seems overly complicated and inefficent. 624 | // detections is an array of objects, for example 625 | // [ { w: '1920', h: '1072', x: '0', y: '4' }, 626 | // { w: '1920', h: '1072', x: '0', y: '4' }, 627 | // { w: '1920', h: '1076', x: '0', y: '2' } ] 628 | function cropDetectionComplete(detections) { 629 | let width = Number.NEGATIVE_INFINITY, 630 | height = Number.NEGATIVE_INFINITY, 631 | x = Number.POSITIVE_INFINITY, 632 | y = Number.POSITIVE_INFINITY; 633 | _.each(detections, function(val, key) { 634 | if (val.w > width) { 635 | width = val.w; 636 | x = val.x; 637 | } 638 | if (val.h > height) { 639 | height = val.h; 640 | y = val.y; 641 | } 642 | }); 643 | 644 | if (width !== _self.videoStream.width || height !== _self.videoStream.height) { 645 | _self.encoder.logger.alert('Output will be cropped to', width + 'x' + height + '.', 'Originally', _self.videoStream.width + 'x' + _self.videoStream.height); 646 | _self.ffmpegCommand.videoFilters('crop=' + width + ':' + height + ':' + x + ':' + y); 647 | } 648 | resolve(); 649 | } 650 | }); 651 | } 652 | 653 | deinterlace() { 654 | let _self = this; 655 | return new Promise(function(resolve, reject) { 656 | if (_self.options.normalizeLevel < 3) 657 | return resolve(); 658 | 659 | let framesToScan = 250; 660 | _self.stages[_self.currentStageNum] = ffmpeg(_self.path).videoFilters('idet').frames(framesToScan).outputOptions('-map v') 661 | .format('rawvideo').outputFormat('null').output('-') 662 | .on('start', function(commandLine) { 663 | if (_self.paused) 664 | _self.pause(); 665 | 666 | _self.encoder.logger.debug('Running Query:', commandLine); 667 | }) 668 | .on('end', function(stdout, stderr) { 669 | let lines = stderr.split('\n'); 670 | let lastLine = lines[lines.length - 2].replace(/ /g, ''); 671 | let numFrames = lastLine.match(new RegExp('TFF:([0-9]+)BFF:([0-9]+)Progressive:([0-9]+)')); 672 | let interlacedFrameCount = parseInt(numFrames[1]) + parseInt(numFrames[2]); 673 | let progressiveFrameCount = parseInt(numFrames[3]); 674 | 675 | if (interlacedFrameCount >= progressiveFrameCount) { 676 | _self.interlaced = true; 677 | _self.ffmpegCommand.videoFilters('yadif'); 678 | _self.encoder.logger.alert('Interlaced video detected. Output will be deinterlaced.'); 679 | return resolve(); 680 | } else { 681 | _self.interlaced = false; 682 | return resolve(); 683 | } 684 | }) 685 | .on('error', function(err, stdout, stderr) { 686 | _self.encoder.logger.debug(err.stack); 687 | 688 | if (err.message.startsWith('ffmpeg was killed with signal')) 689 | return reject(new Error('ENDING')); 690 | else 691 | return reject(err); 692 | }) 693 | .run(); 694 | }); 695 | } 696 | 697 | mapStreams() { 698 | let _self = this; 699 | return new Promise(function(resolve, reject) { 700 | 701 | _self.ffmpegCommand.outputOptions('-map', _self.videoStream.input + ':' + _self.videoStream.index); 702 | _self.encoder.logger.debug('Video stream', _self.videoStream.input + ':' + _self.videoStream.index, 'mapped.', { 703 | size: _self.videoStream.width + 'x' + _self.videoStream.height, 704 | codec: _self.videoStream.codec_long_name, 705 | profile: _self.videoStream.profile, 706 | 'bit depth': _self.videoBitDepth 707 | }); 708 | 709 | // Handle native language detection and default audio track selection 710 | _.each(_self.streams.audioStreams, function(stream, i) { 711 | let normalizedLanguage = helpers.normalizeStreamLanguage(stream); 712 | if (normalizedLanguage === helpers.normalizeLanguage(_self.options['native-language']) && !_self.defaultAudioIndex) { 713 | _self.defaultAudioIndex = stream.index; 714 | } 715 | }); 716 | 717 | // Audio streams 718 | _.each(_self.streams.audioStreams, function(stream, i) { 719 | 720 | let audioTitle = helpers.getStreamTitle(stream); 721 | let normalizedLanguage = helpers.normalizeStreamLanguage(stream); 722 | 723 | _self.ffmpegCommand.outputOptions('-map', stream.input + ':' + stream.index); 724 | if (!(audioTitle) && _self.options.normalizeLevel >= 2) { 725 | 726 | let channelsFormated = helpers.getFormatedChannels(stream.channels); 727 | audioTitle = normalizedLanguage + ' ' + stream.codec_name.toUpperCase() + ((stream.profile && stream.profile !== 'unknown') ? (' ' + stream.profile) : '') + ' (' + channelsFormated + ')'; 728 | _self.encoder.logger.alert('Audio does not have a title. Title set to', '"' + audioTitle + '".'); 729 | _self.ffmpegCommand.outputOptions('-metadata:s:' + stream.index, 'title="' + audioTitle + '"'); 730 | } 731 | 732 | // Set default audio 733 | if (_self.defaultAudioIndex && _self.defaultAudioIndex === stream.index) { 734 | _self.ffmpegCommand.outputOptions('-disposition:a:' + stream.index, 'default'); 735 | } 736 | 737 | let extraInfo = { 738 | title: audioTitle, 739 | language: normalizedLanguage, 740 | codec: stream.codec_long_name, 741 | channels: stream.channels 742 | }; 743 | 744 | if (stream.profile) 745 | extraInfo.profile = stream.profile; 746 | else 747 | extraInfo['bit-depth'] = stream.bits_per_raw_sample; 748 | 749 | _self.encoder.logger.debug('Audio stream', stream.input + ':' + stream.index, 'mapped.', extraInfo); 750 | }); 751 | 752 | // Subtitle streams 753 | _.each(_self.streams.subtitleStreams, function(stream, i) { 754 | 755 | // Handle native language 756 | let normalizedLanguage = helpers.normalizeStreamLanguage(stream); 757 | if (normalizedLanguage === helpers.normalizeLanguage(_self.options['native-language']) && !_self.defaultAudioIndex && !_self.defaultSubtitleIndex) { 758 | _self.defaultSubtitleIndex = stream.index; 759 | } 760 | 761 | _self.ffmpegCommand.outputOptions('-map', stream.input + ':' + stream.index); 762 | if (!helpers.getStreamTitle(stream) && _self.options.normalizeLevel >= 2) { 763 | _self.encoder.logger.alert('Subtitle does not have a title. Title set to', normalizedLanguage + '.'); 764 | _self.ffmpegCommand.outputOptions('-metadata:s:' + stream.index, 'title=' + normalizedLanguage); 765 | } 766 | _self.ffmpegCommand.outputOptions('-disposition:s:' + stream.index, 'default'); 767 | _self.encoder.logger.debug('Subtitle stream', stream.input + ':' + stream.index, 'mapped.', { 768 | title: helpers.getStreamTitle(stream) || normalizedLanguage, 769 | language: normalizedLanguage, 770 | codec: stream.codec_long_name 771 | }); 772 | }); 773 | 774 | // Other streams (Attachments: fonts, pictures, etc.) 775 | _.each(_self.streams.otherStreams, function(stream, i) { 776 | _self.ffmpegCommand.outputOptions('-map', stream.input + ':' + stream.index); 777 | _self.encoder.logger.debug('Other stream', stream.input + ':' + stream.index, 'mapped.'); 778 | }); 779 | 780 | resolve(); 781 | }); 782 | } 783 | 784 | heAudio() { 785 | let _self = this; 786 | return new Promise(function(resolve, reject) { 787 | if (!_self.options.heAudio) 788 | return resolve(); 789 | 790 | _.each(_self.streams.audioStreams, function(stream, i) { 791 | if (stream.codec_name !== 'flac' || _self.options.forceHeAudio) { 792 | _self.encoder.logger.verbose('Audio stream', colors.yellow(helpers.getStreamTitle(stream) + ' (index: ' + stream.index + ')'), 'will be encoded to HE Audio.'); 793 | _self.ffmpegCommand.outputOptions('-c:a:' + i, 'libopus'); 794 | _self.ffmpegCommand.outputOptions('-af', "aformat=channel_layouts='7.1|5.1|stereo'"); 795 | _self.ffmpegCommand.outputOptions('-frame_duration', 60); 796 | if (_self.options.downmixHeAudio && stream.channels > 3) { 797 | // Downmix HE Audio 798 | _self.ffmpegCommand.audioChannels(2).audioFilters('aresample=matrix_encoding=dplii'); 799 | stream.channels = 2; 800 | } 801 | 802 | let bitrate = _self.options.HEAudioBitrate * stream.channels; 803 | _self.ffmpegCommand.outputOptions('-b:a:' + i, bitrate + 'k'); 804 | 805 | // Handle setting a new title 806 | let audioTitle = helpers.getStreamTitle(stream); 807 | let normalizedLanguage = helpers.normalizeStreamLanguage(stream); 808 | if (!(audioTitle) && _self.options.normalizeLevel >= 2) { 809 | let channelsFormated = helpers.getFormatedChannels(stream.channels); 810 | audioTitle = normalizedLanguage + ' OPUS (' + channelsFormated + ')'; 811 | _self.encoder.logger.alert('Audio does not have a title. Title set to', '"' + audioTitle + '".'); 812 | _self.ffmpegCommand.outputOptions('-metadata:s:' + stream.index, 'title="' + audioTitle + '"'); 813 | } 814 | } else { 815 | _self.encoder.logger.alert('Audio stream', colors.yellow(helpers.getStreamTitle(stream) + ' (index: ' + stream.index + ')'), 'won\'t be encoded with HE Audio because it is in a lossless format.'); 816 | } 817 | }); 818 | 819 | resolve(); 820 | }); 821 | } 822 | 823 | encode() { 824 | let _self = this; 825 | return new Promise(function(resolve, reject) { 826 | _self.pass++; 827 | let startTime = moment(); 828 | 829 | if (_self.options.test) 830 | return reject(new Error('Test mode! Skipping...')); 831 | 832 | // Make output directory 833 | if (!_self.options.delete) 834 | fs.ensureDir(_self.output.dir, function(err) { 835 | if (err) { 836 | _self.encoder.logger.warn(_self.base, 'was unable to be encoded. The following error was given:'); 837 | _self.encoder.logger.warn(err); 838 | } 839 | }); 840 | 841 | let frameRate = math.eval(_self.videoStream.avg_frame_rate); 842 | 843 | // Accurate Timestamps 844 | if (_self.options.accurateTimestamps) 845 | _self._addX265Option('keyint=' + math.eval(_self.videoStream['avg_frame_rate']).toFixed(0)); 846 | 847 | // Scale video 848 | if (_self.options.scale) 849 | _self.ffmpegCommand.videoFilters('scale=-1:' + _self.options.scale); 850 | 851 | // Video bitrate target or constant quality? 852 | if (_self.options.videoBitrate) { 853 | _self.ffmpegCommand.videoBitrate(_self.options.videoBitrate); 854 | } else { 855 | _self.ffmpegCommand.outputOptions('-crf ' + _self.options.quality); 856 | } 857 | 858 | // H265 preset 859 | if (_self.options.preset) 860 | _self.ffmpegCommand.outputOptions('-preset', _self.options.preset); 861 | 862 | // H265 extra options 863 | if (_self.options.extraOptions) 864 | _self._addX265Option(_self.options.extraOptions); 865 | 866 | if (_self.x265Options) 867 | _self.ffmpegCommand.outputOptions('-x265-params', _self.x265Options); 868 | 869 | _self.ffmpegCommand 870 | .on('progress', function(progress) { 871 | let elapsed = moment.duration(moment().diff(startTime), 'milliseconds'); 872 | let processed = helpers.momentizeTimemark(progress.timemark); 873 | let precent = progress.percent ? progress.percent.toFixed(1) : ((processed.asMilliseconds() / 1000 / _self.metadata.format.duration) * 100).toFixed(1); 874 | let estimatedFileSize = precent > 10 ? filesize(fs.statSync(_self.output.path).size / precent * 100) : ''; 875 | _self.elapsedFormated = elapsed.format('hh:mm:ss', { 876 | trim: false, 877 | forceLength: true 878 | }); 879 | 880 | // let speed = 'x' + getSpeedRatio(progress.timemark, elapsed); 881 | let speed = (progress.currentFps / frameRate).toFixed(3); 882 | let eta = moment.duration((100 - precent) / 100 * _self.metadata.format.duration * (1 / speed), 'seconds').format('hh:mm:ss', { 883 | trim: false, 884 | forceLength: true 885 | }); 886 | 887 | _self.encoder.logger.info(colors.bgMagenta.white('[ffmpeg]'), 'Processing:', progress.currentFps + 'fps', precent + '%', 888 | '[' + progress.timemark + ']', '|', colors.yellow(_self.elapsedFormated), '[x' + speed + ']', colors.blue(eta), colors.blue(estimatedFileSize), { 889 | __clearLine: true 890 | }); 891 | 892 | _self.progress = { 893 | fps: progress.currentFps, 894 | percent: precent, 895 | processed: processed, 896 | frames: progress.frames, 897 | elapsed: _self.elapsedFormated, 898 | eta: eta, 899 | speed: speed 900 | }; 901 | 902 | _self.events.emit('progress', _self.progress); 903 | }) 904 | .on('start', function(commandLine) { 905 | if (_self.paused) 906 | _self.pause(); 907 | _self.temp.files.push(_self.output.path); 908 | _self.encoder.logger.debug('Running Query:', commandLine); 909 | }) 910 | // .on('stderr', function(stderrLine) { 911 | // _self.encoder.logger.info(colors.bgMagenta.white('[ffmpeg]'), stderrLine); 912 | // }) 913 | .on('end', function() { 914 | _.pull(_self.temp.files, _self.output.path); 915 | resolve(); 916 | }) 917 | .on('error', function(err, stdout, stderr) { 918 | 919 | if (err.message.startsWith('ffmpeg was killed with signal')) 920 | reject(new Error('FFMPEGKILLED')); 921 | else 922 | reject(new Error('ffmpeg exited with an error.' + stdout + stderr)); 923 | }); 924 | _self.ffmpegCommand.output(_self.output.path, { 925 | end: true 926 | }); 927 | 928 | if (_self.encoder.enablePreviewStream) 929 | _self.ffmpegCommand 930 | .output(_self.previewStream) 931 | .noAudio() 932 | .videoCodec('bmp') 933 | .format('image2') 934 | .outputOptions('-vf', 'fps=3 [slow];[slow] scale=-1:240') 935 | .outputOptions('-pix_fmt', 'bgr24') 936 | .outputOptions('-updatefirst 1'); 937 | 938 | // Preview Mode 939 | if (_self.options.preview) { 940 | _self.ffmpegCommand.seekInput(_self.metadata.format.duration / 2).duration(_self.options.previewLength / 1000); 941 | } 942 | _self.ffmpegCommand.run(); 943 | }); 944 | } 945 | 946 | multiPass() { 947 | let _self = this; 948 | return new Promise(function(resolve, reject) { 949 | 950 | if (!_self.options.multiPass || _self.options.multiPass <= 1) 951 | return resolve(); 952 | 953 | if (!_self.options.videoBitrate) 954 | return reject(new Error('Multipass is only compatable with bitrate encoding, not constant quality encoding.')); 955 | 956 | if (_self.pass >= _self.options.multiPass) 957 | return resolve(); 958 | 959 | let newInput = _self.output.path + '-pass' + _self.pass; 960 | _self.ffmpegCommand = new ffmpeg(newInput, { 961 | logger: _self.encoder.logger 962 | }) 963 | //.inputOptions('-loglevel', 48) 964 | .outputOptions('-map', 0) 965 | .outputOptions('-c', 'copy') 966 | .outputOptions('-c:v', 'libx265'); 967 | 968 | let statsLogLocation = Path.resolve(os.tmpdir(), 'x265stats.log'); 969 | 970 | if (_self.options.multiPass === _self.pass + 1) { 971 | _self._addX265Option('pass=2:stats=' + statsLogLocation); 972 | } else { 973 | _self._addX265Option('pass=3:stats=' + statsLogLocation); 974 | } 975 | 976 | return fs.move(_self.output.path, newInput, { 977 | clobber: true 978 | }, function(err) { 979 | if (err) { 980 | _self.encoder.logger.error(err.message); 981 | _self.encoder.logger.debug(err.stack); 982 | 983 | return reject(new Error('Error moving file ' + colors.yellow(_self.output.path) + ' to ' + colors.yellow(newInput) + '.')); 984 | } 985 | 986 | _self._updatePath(newInput); 987 | _self.temp.files.push(statsLogLocation); 988 | _self.temp.files.push(newInput); 989 | 990 | _self.encoder.logger.verbose('Running pass', _self.pass + 1 + '.'); 991 | return _self.setVideoBitDepth().then(function() { 992 | return _self.encode.call(_self); 993 | }).then(function() { 994 | return _self.multiPass.call(_self); 995 | }).then(resolve, reject); 996 | }); 997 | }); 998 | } 999 | 1000 | verifyEncode() { 1001 | let _self = this; 1002 | return new Promise(function(resolve, reject) { 1003 | Video.getMetadata(_self, true).then(function(metadata) { 1004 | const oldMetadata = _self.metadata; 1005 | const timeDiffLimit = 1; // in seconds 1006 | _self.output.metadata = metadata; 1007 | _self.ratio = (_self.output.metadata.format.size / oldMetadata.format.size * 100).toFixed(2); 1008 | _self.encoder.logger.debug('Original Duration:', oldMetadata.format.duration + '(s)\t', 'New Duration:', metadata.format.duration + '(s)'); 1009 | let timeDiff = oldMetadata.format.duration - metadata.format.duration; 1010 | // TODO: TimeDiffLimit 1011 | if (timeDiff > timeDiffLimit && !_self.options.preview) { 1012 | _self.encoder.logger.warn('New encode is', timeDiff, 'seconds longer than the original. The max is', timeDiffLimit, 'seconds.'); 1013 | fs.unlinkSync(_self.output.path); 1014 | reject(new Error('Processed encode did not meet max time slippage requirements.')); 1015 | } else { 1016 | resolve(); 1017 | } 1018 | }); 1019 | }); 1020 | } 1021 | 1022 | move() { 1023 | let _self = this; 1024 | return new Promise(function(resolve, reject) { 1025 | if (!_self.options.delete) 1026 | return resolve(); 1027 | 1028 | const newDestination = Path.join(_self.dir, _self.output.base); 1029 | _self.encoder.logger.debug('Removing original and moving file to ', newDestination); 1030 | fs.remove(_self.path, function(err) { 1031 | if (err) return reject(err); 1032 | _self.encoder.watchIgnore.push(newDestination); 1033 | fs.move(_self.output.path, newDestination, 1034 | function(err) { 1035 | if (err) return reject(err); 1036 | _self._updateDestination(_self.dir); 1037 | resolve(); 1038 | }); 1039 | }) 1040 | }); 1041 | } 1042 | 1043 | screenshots() { 1044 | let _self = this; 1045 | return new Promise(function(resolve, reject) { 1046 | if (!_self.options.screenshots) 1047 | return resolve(); 1048 | 1049 | Video.takeScreenshots(_self.output.path, Path.join(_self.output.dir, 'screenshots'), _self.encoder.logger).then(resolve, reject); 1050 | }); 1051 | } 1052 | 1053 | sample() { 1054 | let _self = this; 1055 | return new Promise(function(resolve, reject) { 1056 | if (!_self.options.sample) 1057 | return resolve(); 1058 | 1059 | if (_self.options.preview) { 1060 | _self.encoder.logger.warn('Cannot create sample: Preview and sample flags cannot be used together.'); 1061 | return resolve(); 1062 | } 1063 | 1064 | let startTime = moment(); 1065 | let frameRate = math.eval(_self.videoStream.avg_frame_rate); 1066 | let command = _self.stages[_self.currentStageNum].command = new ffmpeg(_self.path, { 1067 | logger: _self.encoder.logger 1068 | }).seekInput(_self.metadata.format.duration / 2).duration(_self.options.previewLength / 1000).outputOptions('-c', 'copy').output(_self.output.sample); 1069 | 1070 | command 1071 | .on('progress', function(progress) { 1072 | let elapsed = moment.duration(moment().diff(startTime), 'milliseconds'); 1073 | let processed = helpers.momentizeTimemark(progress.timemark); 1074 | let precent = progress.percent ? progress.percent.toFixed(1) : ((processed.asMilliseconds() / 1000 / _self.metadata.format.duration) * 100).toFixed(1); 1075 | _self.elapsedFormated = elapsed.format('hh:mm:ss', { 1076 | trim: false, 1077 | forceLength: true 1078 | }); 1079 | 1080 | // let speed = 'x' + getSpeedRatio(progress.timemark, elapsed); 1081 | let speed = (progress.currentFps / frameRate).toFixed(3); 1082 | let eta = moment.duration((100 - precent) / 100 * _self.metadata.format.duration * (1 / speed), 'seconds').format('hh:mm:ss', { 1083 | trim: false, 1084 | forceLength: true 1085 | }); 1086 | 1087 | _self.encoder.logger.info(colors.bgMagenta.white('[ffmpeg]'), 'Processing:', progress.currentFps + 'fps', precent + '%', 1088 | '[' + progress.timemark + ']', '|', colors.yellow(_self.elapsedFormated), '[x' + speed + ']', colors.blue(eta), { 1089 | __clearLine: true 1090 | }); 1091 | 1092 | _self.progress = { 1093 | fps: progress.currentFps, 1094 | percent: precent, 1095 | processed: processed, 1096 | elapsed: _self.elapsedFormated, 1097 | eta: eta, 1098 | speed: speed 1099 | }; 1100 | 1101 | _self.events.emit('progress', _self.progress); 1102 | }) 1103 | .on('start', function(commandLine) { 1104 | if (_self.paused) 1105 | _self.pause(); 1106 | _self.temp.files.push(_self.output.path); 1107 | _self.encoder.logger.debug('Running Query:', commandLine); 1108 | }) 1109 | // .on('stderr', function(stderrLine) { 1110 | // _self.encoder.logger.info(colors.bgMagenta.white('[ffmpeg]'), stderrLine); 1111 | // }) 1112 | .on('end', function() { 1113 | _.pull(_self.temp.files, _self.output.path); 1114 | resolve(); 1115 | }) 1116 | .on('error', function(err, stdout, stderr) { 1117 | // this.encoder.logger.debug(colors.bgMagenta.white('[ffmpeg]'), stderr); 1118 | _self.encoder.logger.debug(err.stack); 1119 | 1120 | if (err.message.startsWith('ffmpeg was killed with signal')) 1121 | reject(new Error('FFMPEGKILLED')); 1122 | else 1123 | reject(err); 1124 | }); 1125 | }); 1126 | } 1127 | 1128 | appendStats() { 1129 | let _self = this; 1130 | return new Promise(function(resolve, reject) { 1131 | if (!_self.options.stats) 1132 | return resolve(); 1133 | 1134 | helpers.initStatsFile(Path.join(process.cwd(), 'h265ize.csv')).then(function(stream) { 1135 | helpers.appendToStatsFile([ 1136 | moment().format('MMMM Do YYYY H:mm:ss a'), 1137 | _self.path, 1138 | filesize(_self.metadata.format.size), 1139 | filesize(_self.output.metadata.format.size), 1140 | _self.ratio + '%', 1141 | _self.elapsedFormated 1142 | ], stream) 1143 | .then(function() { 1144 | stream.close(); 1145 | resolve(); 1146 | }); 1147 | }); 1148 | 1149 | }); 1150 | } 1151 | 1152 | start() { 1153 | if (this.running) 1154 | return new Error('ALREADYRUNNING'); 1155 | 1156 | if (this.paused) { 1157 | return this.resume(); 1158 | } 1159 | 1160 | this.startTime = moment(); 1161 | this.encoder.logger.verbose('Encoding started at', colors.yellow(this.startTime.format("ddd, h:mm A"))); 1162 | this.running = true; 1163 | 1164 | let _self = this; 1165 | this.promiseChain = Promise.reduce(this.stages, function(stageNum, stage) { 1166 | _self.currentStageNum = _self.currentStageNum + 1; 1167 | _self.currentStage = _self.stages[_self.currentStageNum]; 1168 | 1169 | _self.encoder.logger.verbose('Running stage:', stage.name); 1170 | _self.events.emit('stage', stage.name); 1171 | 1172 | return stage.promise.call(_self).tap(Pauser.waitFor(_self.pauser)); 1173 | 1174 | }, this.currentStageNum).then(function() { 1175 | _self.finishedAt = moment(); 1176 | _self.status = 'finished'; 1177 | _self.events.emit('finished', _self); 1178 | _self.stop(); 1179 | 1180 | }, function(err) { 1181 | _self.error = err; 1182 | _self.events.emit('failed', _self); 1183 | _self.stop(); 1184 | 1185 | }); 1186 | 1187 | this.events.emit('running'); 1188 | } 1189 | 1190 | resume() { 1191 | this.paused = false; 1192 | this.running = true; 1193 | this.pauser.unpause(); 1194 | 1195 | // Check if there is command active 1196 | if (this.stages[this.currentStageNum].command) { 1197 | 1198 | // Resume running command 1199 | this.stages[this.currentStageNum].command.kill('SIGCONT'); 1200 | } 1201 | 1202 | // Special handling for the encode stage 1203 | if (this.stages[this.currentStageNum].name === 'Encode') { 1204 | this.ffmpegCommand.kill('SIGCONT'); 1205 | } 1206 | 1207 | this.encoder.logger.info('Resumed...'); 1208 | this.events.emit('resumed'); 1209 | } 1210 | 1211 | pause() { 1212 | this.paused = true; 1213 | this.running = false; 1214 | this.pauser.pause(); 1215 | 1216 | // Check if there is command active 1217 | if (this.stages[this.currentStageNum].command) { 1218 | 1219 | // Resume running command 1220 | this.stages[this.currentStageNum].command.kill('SIGTSTP'); 1221 | } 1222 | 1223 | // Special handling for the encode stage 1224 | if (this.stages[this.currentStageNum].name === 'Encode') { 1225 | this.ffmpegCommand.kill('SIGTSTP'); 1226 | } 1227 | 1228 | this.encoder.logger.info('Paused...'); 1229 | this.events.emit('paused'); 1230 | } 1231 | 1232 | stop() { 1233 | 1234 | if (!this.running) 1235 | return new Error('NOTRUNNING'); 1236 | 1237 | this.running = false; 1238 | this.paused = false; 1239 | 1240 | // End encoding 1241 | if (this.ffmpegCommand) 1242 | this.ffmpegCommand.kill(); 1243 | 1244 | this.promiseChain.cancel(); 1245 | 1246 | if (!this.error && this.status !== 'finished') { 1247 | this.status = 'failed'; 1248 | this.error = new Error('Stopped prematurely.'); 1249 | this.events.emit('failed', this); 1250 | } 1251 | 1252 | return this.cleanUp(); 1253 | } 1254 | 1255 | cleanUp() { 1256 | let _self = this; 1257 | return new Promise(function(resolve, reject) { 1258 | _.each(_self.temp.files, function(path, i) { 1259 | fs.unlinkSync(path); 1260 | }); 1261 | resolve(); 1262 | }); 1263 | } 1264 | 1265 | _updatePath(path) { 1266 | this.path = path; 1267 | 1268 | let pathParsed = Path.parse(this.path); 1269 | this.base = pathParsed.base; 1270 | this.dir = pathParsed.dir; 1271 | this.ext = pathParsed.ext; 1272 | this.name = pathParsed.name; 1273 | this.root = pathParsed.root; 1274 | } 1275 | 1276 | _updateDestination(path) { 1277 | this.options.destination = path; 1278 | this.output = { 1279 | base: this.name + (this.options.preview ? '-preview' : '') + '.' + this.options.outputFormat, 1280 | dir: this.options.destination, 1281 | sample: this.name + '-sample.' + this.options.outputFormat 1282 | }; 1283 | this.output.path = Path.join(this.output.dir, this.output.base); 1284 | } 1285 | 1286 | static getMetadata(video, ofOutput) { 1287 | return new Promise(function(resolve, reject) { 1288 | let path = ofOutput ? video.output.path : video.path; 1289 | ffmpeg.ffprobe(path, function(err, metadata) { 1290 | if (err) { 1291 | // video.encoder.logger.error(err.message); 1292 | // video.encoder.logger.debug('ffprobe error stack:', err.stack); 1293 | return reject(err); 1294 | } 1295 | 1296 | // video.encoder.logger.debug('Container data:', { 1297 | // duration: moment.duration(metadata.format.duration, 'seconds').format('hh:mm:ss', { 1298 | // trim: false, 1299 | // forceLength: true 1300 | // }), 1301 | // size: filesize(metadata.format.size) 1302 | // }); 1303 | 1304 | if (metadata.format.format_name !== 'srt' && !_.isNumber(metadata.format.duration)) { 1305 | // video.encoder.logger.alert('Could not retrieve video duration. Computing duration...'); 1306 | video.stages[video.currentStageNum].command = new ffmpeg().input(video.path).outputFormat('null').output('-').on('start', function() { 1307 | if (video.paused) 1308 | video.pause(); 1309 | }).on('end', function(stdout, stderr) { 1310 | delete video.stages[video.currentStageNum].command; 1311 | let lines = stderr.split('\n'); 1312 | let lastTime = lines[lines.length - 3]; 1313 | let duration = lastTime.match(new RegExp('time=(([0-9]|\:|\.)+) bitrate'))[1]; 1314 | 1315 | // Fix bug with momentjs https://github.com/moment/moment/issues/3266 1316 | if (duration.indexOf('.') <= duration.length - 3) { 1317 | duration += '000'; 1318 | } 1319 | 1320 | let seconds = moment.duration(duration); 1321 | metadata.format.duration = seconds.format("s", 6); 1322 | resolve(metadata); 1323 | }).run(); 1324 | } else { 1325 | resolve(metadata); 1326 | } 1327 | }); 1328 | }); 1329 | } 1330 | 1331 | static takeScreenshots(path, destination, logger) { 1332 | return new Promise(function(resolve, reject) { 1333 | 1334 | let command = new ffmpeg(path); 1335 | let outputDir = Path.join(destination); 1336 | 1337 | command 1338 | .on('filenames', function(filenames) { 1339 | if (filenames.length < 6) 1340 | logger.alert('Only generating', colors.yellow(filenames.length), 'screenshots.'); 1341 | }) 1342 | .on('end', function() { 1343 | resolve(); 1344 | }); 1345 | 1346 | fs.ensureDir(outputDir, function(err) { 1347 | if (err) { 1348 | throw err; 1349 | } 1350 | 1351 | command.screenshots({ 1352 | filename: '%b-%i.png', 1353 | folder: outputDir, 1354 | count: 6 1355 | }); 1356 | }); 1357 | 1358 | }); 1359 | } 1360 | } 1361 | 1362 | module.exports = Video; 1363 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | abbrev@1: 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 8 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 9 | 10 | ansi-regex@^2.0.0: 11 | version "2.1.1" 12 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 13 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 14 | 15 | ansi-regex@^3.0.0: 16 | version "3.0.0" 17 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 18 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 19 | 20 | anymatch@^1.3.0: 21 | version "1.3.2" 22 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" 23 | integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== 24 | dependencies: 25 | micromatch "^2.1.5" 26 | normalize-path "^2.0.0" 27 | 28 | aproba@^1.0.3: 29 | version "1.2.0" 30 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" 31 | integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== 32 | 33 | are-we-there-yet@~1.1.2: 34 | version "1.1.5" 35 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" 36 | integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== 37 | dependencies: 38 | delegates "^1.0.0" 39 | readable-stream "^2.0.6" 40 | 41 | arr-diff@^2.0.0: 42 | version "2.0.0" 43 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 44 | integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= 45 | dependencies: 46 | arr-flatten "^1.0.1" 47 | 48 | arr-diff@^4.0.0: 49 | version "4.0.0" 50 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" 51 | integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= 52 | 53 | arr-flatten@^1.0.1, arr-flatten@^1.1.0: 54 | version "1.1.0" 55 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" 56 | integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== 57 | 58 | arr-union@^3.1.0: 59 | version "3.1.0" 60 | resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 61 | integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= 62 | 63 | array-unique@^0.2.1: 64 | version "0.2.1" 65 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 66 | integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= 67 | 68 | array-unique@^0.3.2: 69 | version "0.3.2" 70 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 71 | integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= 72 | 73 | assertion-error@^1.0.1: 74 | version "1.1.0" 75 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 76 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 77 | 78 | assign-symbols@^1.0.0: 79 | version "1.0.0" 80 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" 81 | integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= 82 | 83 | async-each@^1.0.0: 84 | version "1.0.3" 85 | resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" 86 | integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== 87 | 88 | async@>=0.2.9: 89 | version "3.1.0" 90 | resolved "https://registry.yarnpkg.com/async/-/async-3.1.0.tgz#42b3b12ae1b74927b5217d8c0016baaf62463772" 91 | integrity sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ== 92 | 93 | async@~1.0.0: 94 | version "1.0.0" 95 | resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" 96 | integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k= 97 | 98 | async@~1.5: 99 | version "1.5.2" 100 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 101 | integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= 102 | 103 | atob@^2.1.1: 104 | version "2.1.2" 105 | resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" 106 | integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== 107 | 108 | balanced-match@^1.0.0: 109 | version "1.0.0" 110 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 111 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 112 | 113 | base@^0.11.1: 114 | version "0.11.2" 115 | resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" 116 | integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== 117 | dependencies: 118 | cache-base "^1.0.1" 119 | class-utils "^0.3.5" 120 | component-emitter "^1.2.1" 121 | define-property "^1.0.0" 122 | isobject "^3.0.1" 123 | mixin-deep "^1.2.0" 124 | pascalcase "^0.1.1" 125 | 126 | binary-extensions@^1.0.0: 127 | version "1.13.1" 128 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" 129 | integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== 130 | 131 | bluebird@^2.9.25: 132 | version "2.11.0" 133 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" 134 | integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= 135 | 136 | bluebird@^3.4.6: 137 | version "3.7.2" 138 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" 139 | integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== 140 | 141 | brace-expansion@^1.1.7: 142 | version "1.1.11" 143 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 144 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 145 | dependencies: 146 | balanced-match "^1.0.0" 147 | concat-map "0.0.1" 148 | 149 | braces@^1.8.2: 150 | version "1.8.5" 151 | resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 152 | integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= 153 | dependencies: 154 | expand-range "^1.8.1" 155 | preserve "^0.2.0" 156 | repeat-element "^1.1.2" 157 | 158 | braces@^2.3.1: 159 | version "2.3.2" 160 | resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" 161 | integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== 162 | dependencies: 163 | arr-flatten "^1.1.0" 164 | array-unique "^0.3.2" 165 | extend-shallow "^2.0.1" 166 | fill-range "^4.0.0" 167 | isobject "^3.0.1" 168 | repeat-element "^1.1.2" 169 | snapdragon "^0.8.1" 170 | snapdragon-node "^2.0.1" 171 | split-string "^3.0.2" 172 | to-regex "^3.0.1" 173 | 174 | browser-stdout@1.3.0: 175 | version "1.3.0" 176 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 177 | integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= 178 | 179 | cache-base@^1.0.1: 180 | version "1.0.1" 181 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 182 | integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== 183 | dependencies: 184 | collection-visit "^1.0.0" 185 | component-emitter "^1.2.1" 186 | get-value "^2.0.6" 187 | has-value "^1.0.0" 188 | isobject "^3.0.1" 189 | set-value "^2.0.0" 190 | to-object-path "^0.3.0" 191 | union-value "^1.0.0" 192 | unset-value "^1.0.0" 193 | 194 | camelcase@^3.0.0: 195 | version "3.0.0" 196 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" 197 | integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= 198 | 199 | chai@^3.5.0: 200 | version "3.5.0" 201 | resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" 202 | integrity sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc= 203 | dependencies: 204 | assertion-error "^1.0.1" 205 | deep-eql "^0.1.3" 206 | type-detect "^1.0.0" 207 | 208 | chokidar@^1.6.1: 209 | version "1.7.0" 210 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" 211 | integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= 212 | dependencies: 213 | anymatch "^1.3.0" 214 | async-each "^1.0.0" 215 | glob-parent "^2.0.0" 216 | inherits "^2.0.1" 217 | is-binary-path "^1.0.0" 218 | is-glob "^2.0.0" 219 | path-is-absolute "^1.0.0" 220 | readdirp "^2.0.0" 221 | optionalDependencies: 222 | fsevents "^1.0.0" 223 | 224 | chownr@^1.1.1: 225 | version "1.1.3" 226 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" 227 | integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== 228 | 229 | class-utils@^0.3.5: 230 | version "0.3.6" 231 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 232 | integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== 233 | dependencies: 234 | arr-union "^3.1.0" 235 | define-property "^0.2.5" 236 | isobject "^3.0.0" 237 | static-extend "^0.1.1" 238 | 239 | cliui@^3.2.0: 240 | version "3.2.0" 241 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" 242 | integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= 243 | dependencies: 244 | string-width "^1.0.1" 245 | strip-ansi "^3.0.1" 246 | wrap-ansi "^2.0.0" 247 | 248 | code-point-at@^1.0.0: 249 | version "1.1.0" 250 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 251 | integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= 252 | 253 | collection-visit@^1.0.0: 254 | version "1.0.0" 255 | resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" 256 | integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= 257 | dependencies: 258 | map-visit "^1.0.0" 259 | object-visit "^1.0.0" 260 | 261 | colors@1.0.x: 262 | version "1.0.3" 263 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 264 | integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= 265 | 266 | colors@^1.1.2: 267 | version "1.4.0" 268 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 269 | integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== 270 | 271 | commander@2.9.0: 272 | version "2.9.0" 273 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 274 | integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= 275 | dependencies: 276 | graceful-readlink ">= 1.0.0" 277 | 278 | complex.js@2.0.4: 279 | version "2.0.4" 280 | resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.4.tgz#d8e7cfb9652d1e853e723386421c1a0ca7a48373" 281 | integrity sha512-Syl95HpxUTS0QjwNxencZsKukgh1zdS9uXeXX2Us0pHaqBR6kiZZi0AkZ9VpZFwHJyVIUVzI4EumjWdXP3fy6w== 282 | 283 | component-emitter@^1.2.1: 284 | version "1.3.0" 285 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" 286 | integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== 287 | 288 | concat-map@0.0.1: 289 | version "0.0.1" 290 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 291 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 292 | 293 | console-control-strings@^1.0.0, console-control-strings@~1.1.0: 294 | version "1.1.0" 295 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 296 | integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= 297 | 298 | copy-descriptor@^0.1.0: 299 | version "0.1.1" 300 | resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 301 | integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= 302 | 303 | core-util-is@~1.0.0: 304 | version "1.0.2" 305 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 306 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 307 | 308 | cycle@1.0.x: 309 | version "1.0.3" 310 | resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" 311 | integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= 312 | 313 | debug@2.6.8: 314 | version "2.6.8" 315 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 316 | integrity sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw= 317 | dependencies: 318 | ms "2.0.0" 319 | 320 | debug@^2.2.0, debug@^2.3.3: 321 | version "2.6.9" 322 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 323 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 324 | dependencies: 325 | ms "2.0.0" 326 | 327 | debug@^3.2.6: 328 | version "3.2.6" 329 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 330 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 331 | dependencies: 332 | ms "^2.1.1" 333 | 334 | decamelize@^1.1.1: 335 | version "1.2.0" 336 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 337 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 338 | 339 | decimal.js@9.0.1: 340 | version "9.0.1" 341 | resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-9.0.1.tgz#1cc8b228177da7ab6498c1cc06eb130a290e6e1e" 342 | integrity sha512-2h0iKbJwnImBk4TGk7CG1xadoA0g3LDPlQhQzbZ221zvG0p2YVUedbKIPsOZXKZGx6YmZMJKYOalpCMxSdDqTQ== 343 | 344 | decode-uri-component@^0.2.0: 345 | version "0.2.0" 346 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" 347 | integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= 348 | 349 | deep-eql@^0.1.3: 350 | version "0.1.3" 351 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" 352 | integrity sha1-71WKyrjeJSBs1xOQbXTlaTDrafI= 353 | dependencies: 354 | type-detect "0.1.1" 355 | 356 | deep-extend@^0.6.0: 357 | version "0.6.0" 358 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 359 | integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== 360 | 361 | define-property@^0.2.5: 362 | version "0.2.5" 363 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" 364 | integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= 365 | dependencies: 366 | is-descriptor "^0.1.0" 367 | 368 | define-property@^1.0.0: 369 | version "1.0.0" 370 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 371 | integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= 372 | dependencies: 373 | is-descriptor "^1.0.0" 374 | 375 | define-property@^2.0.2: 376 | version "2.0.2" 377 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" 378 | integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== 379 | dependencies: 380 | is-descriptor "^1.0.2" 381 | isobject "^3.0.1" 382 | 383 | delegates@^1.0.0: 384 | version "1.0.0" 385 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 386 | integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= 387 | 388 | detect-libc@^1.0.2: 389 | version "1.0.3" 390 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" 391 | integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= 392 | 393 | diff@3.2.0: 394 | version "3.2.0" 395 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 396 | integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= 397 | 398 | error-ex@^1.2.0: 399 | version "1.3.2" 400 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 401 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 402 | dependencies: 403 | is-arrayish "^0.2.1" 404 | 405 | escape-latex@^1.0.0: 406 | version "1.2.0" 407 | resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" 408 | integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== 409 | 410 | escape-string-regexp@1.0.5: 411 | version "1.0.5" 412 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 413 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 414 | 415 | expand-brackets@^0.1.4: 416 | version "0.1.5" 417 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 418 | integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= 419 | dependencies: 420 | is-posix-bracket "^0.1.0" 421 | 422 | expand-brackets@^2.1.4: 423 | version "2.1.4" 424 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" 425 | integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= 426 | dependencies: 427 | debug "^2.3.3" 428 | define-property "^0.2.5" 429 | extend-shallow "^2.0.1" 430 | posix-character-classes "^0.1.0" 431 | regex-not "^1.0.0" 432 | snapdragon "^0.8.1" 433 | to-regex "^3.0.1" 434 | 435 | expand-range@^1.8.1: 436 | version "1.8.2" 437 | resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 438 | integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= 439 | dependencies: 440 | fill-range "^2.1.0" 441 | 442 | extend-shallow@^2.0.1: 443 | version "2.0.1" 444 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 445 | integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= 446 | dependencies: 447 | is-extendable "^0.1.0" 448 | 449 | extend-shallow@^3.0.0, extend-shallow@^3.0.2: 450 | version "3.0.2" 451 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" 452 | integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= 453 | dependencies: 454 | assign-symbols "^1.0.0" 455 | is-extendable "^1.0.1" 456 | 457 | extglob@^0.3.1: 458 | version "0.3.2" 459 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 460 | integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= 461 | dependencies: 462 | is-extglob "^1.0.0" 463 | 464 | extglob@^2.0.4: 465 | version "2.0.4" 466 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" 467 | integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== 468 | dependencies: 469 | array-unique "^0.3.2" 470 | define-property "^1.0.0" 471 | expand-brackets "^2.1.4" 472 | extend-shallow "^2.0.1" 473 | fragment-cache "^0.2.1" 474 | regex-not "^1.0.0" 475 | snapdragon "^0.8.1" 476 | to-regex "^3.0.1" 477 | 478 | eyes@0.1.x: 479 | version "0.1.8" 480 | resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" 481 | integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= 482 | 483 | filename-regex@^2.0.0: 484 | version "2.0.1" 485 | resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" 486 | integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= 487 | 488 | filesize@^3.3.0: 489 | version "3.6.1" 490 | resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" 491 | integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== 492 | 493 | fill-range@^2.1.0: 494 | version "2.2.4" 495 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" 496 | integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== 497 | dependencies: 498 | is-number "^2.1.0" 499 | isobject "^2.0.0" 500 | randomatic "^3.0.0" 501 | repeat-element "^1.1.2" 502 | repeat-string "^1.5.2" 503 | 504 | fill-range@^4.0.0: 505 | version "4.0.0" 506 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" 507 | integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= 508 | dependencies: 509 | extend-shallow "^2.0.1" 510 | is-number "^3.0.0" 511 | repeat-string "^1.6.1" 512 | to-regex-range "^2.1.0" 513 | 514 | find-up@^1.0.0: 515 | version "1.1.2" 516 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 517 | integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= 518 | dependencies: 519 | path-exists "^2.0.0" 520 | pinkie-promise "^2.0.0" 521 | 522 | fluent-ffmpeg@^2.1.0: 523 | version "2.1.2" 524 | resolved "https://registry.yarnpkg.com/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz#c952de2240f812ebda0aa8006d7776ee2acf7d74" 525 | integrity sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ= 526 | dependencies: 527 | async ">=0.2.9" 528 | which "^1.1.1" 529 | 530 | for-in@^1.0.1, for-in@^1.0.2: 531 | version "1.0.2" 532 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 533 | integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= 534 | 535 | for-own@^0.1.4: 536 | version "0.1.5" 537 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" 538 | integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= 539 | dependencies: 540 | for-in "^1.0.1" 541 | 542 | fraction.js@4.0.4: 543 | version "4.0.4" 544 | resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.4.tgz#04e567110718adf7b52974a10434ab4c67a5183e" 545 | integrity sha512-aK/oGatyYLTtXRHjfEsytX5fieeR5H4s8sLorzcT12taFS+dbMZejnvm9gRa8mZAPwci24ucjq9epDyaq5u8Iw== 546 | 547 | fragment-cache@^0.2.1: 548 | version "0.2.1" 549 | resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" 550 | integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= 551 | dependencies: 552 | map-cache "^0.2.2" 553 | 554 | fs-extra@^1.0.0: 555 | version "1.0.0" 556 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" 557 | integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= 558 | dependencies: 559 | graceful-fs "^4.1.2" 560 | jsonfile "^2.1.0" 561 | klaw "^1.0.0" 562 | 563 | fs-minipass@^1.2.5: 564 | version "1.2.7" 565 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" 566 | integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== 567 | dependencies: 568 | minipass "^2.6.0" 569 | 570 | fs.realpath@^1.0.0: 571 | version "1.0.0" 572 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 573 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 574 | 575 | fsevents@^1.0.0: 576 | version "1.2.9" 577 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" 578 | integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== 579 | dependencies: 580 | nan "^2.12.1" 581 | node-pre-gyp "^0.12.0" 582 | 583 | gauge@~2.7.3: 584 | version "2.7.4" 585 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" 586 | integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= 587 | dependencies: 588 | aproba "^1.0.3" 589 | console-control-strings "^1.0.0" 590 | has-unicode "^2.0.0" 591 | object-assign "^4.1.0" 592 | signal-exit "^3.0.0" 593 | string-width "^1.0.1" 594 | strip-ansi "^3.0.1" 595 | wide-align "^1.1.0" 596 | 597 | get-caller-file@^1.0.1: 598 | version "1.0.3" 599 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" 600 | integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== 601 | 602 | get-value@^2.0.3, get-value@^2.0.6: 603 | version "2.0.6" 604 | resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" 605 | integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= 606 | 607 | glob-base@^0.3.0: 608 | version "0.3.0" 609 | resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 610 | integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= 611 | dependencies: 612 | glob-parent "^2.0.0" 613 | is-glob "^2.0.0" 614 | 615 | glob-parent@^2.0.0: 616 | version "2.0.0" 617 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 618 | integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= 619 | dependencies: 620 | is-glob "^2.0.0" 621 | 622 | glob@7.1.1: 623 | version "7.1.1" 624 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 625 | integrity sha1-gFIR3wT6rxxjo2ADBs31reULLsg= 626 | dependencies: 627 | fs.realpath "^1.0.0" 628 | inflight "^1.0.4" 629 | inherits "2" 630 | minimatch "^3.0.2" 631 | once "^1.3.0" 632 | path-is-absolute "^1.0.0" 633 | 634 | glob@^7.0.0, glob@^7.1.3: 635 | version "7.1.6" 636 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 637 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 638 | dependencies: 639 | fs.realpath "^1.0.0" 640 | inflight "^1.0.4" 641 | inherits "2" 642 | minimatch "^3.0.4" 643 | once "^1.3.0" 644 | path-is-absolute "^1.0.0" 645 | 646 | gpu-info@0.0.1: 647 | version "0.0.1" 648 | resolved "https://registry.yarnpkg.com/gpu-info/-/gpu-info-0.0.1.tgz#527541047b8b16c7165ede473a499d3ff16784c1" 649 | integrity sha1-UnVBBHuLFscWXt5HOkmdP/FnhME= 650 | dependencies: 651 | pegjs "^0.10.0" 652 | 653 | graceful-fs@4.1.6: 654 | version "4.1.6" 655 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.6.tgz#514c38772b31bee2e08bedc21a0aeb3abf54c19e" 656 | integrity sha1-UUw4dysxvuLgi+3CGgrrOr9UwZ4= 657 | 658 | graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: 659 | version "4.2.3" 660 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" 661 | integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== 662 | 663 | "graceful-readlink@>= 1.0.0": 664 | version "1.0.1" 665 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 666 | integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= 667 | 668 | growl@1.9.2: 669 | version "1.9.2" 670 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 671 | integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= 672 | 673 | has-flag@^1.0.0: 674 | version "1.0.0" 675 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 676 | integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= 677 | 678 | has-unicode@^2.0.0: 679 | version "2.0.1" 680 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 681 | integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= 682 | 683 | has-value@^0.3.1: 684 | version "0.3.1" 685 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" 686 | integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= 687 | dependencies: 688 | get-value "^2.0.3" 689 | has-values "^0.1.4" 690 | isobject "^2.0.0" 691 | 692 | has-value@^1.0.0: 693 | version "1.0.0" 694 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" 695 | integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= 696 | dependencies: 697 | get-value "^2.0.6" 698 | has-values "^1.0.0" 699 | isobject "^3.0.0" 700 | 701 | has-values@^0.1.4: 702 | version "0.1.4" 703 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" 704 | integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= 705 | 706 | has-values@^1.0.0: 707 | version "1.0.0" 708 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" 709 | integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= 710 | dependencies: 711 | is-number "^3.0.0" 712 | kind-of "^4.0.0" 713 | 714 | hasbin@^1.2.3: 715 | version "1.2.3" 716 | resolved "https://registry.yarnpkg.com/hasbin/-/hasbin-1.2.3.tgz#78c5926893c80215c2b568ae1fd3fcab7a2696b0" 717 | integrity sha1-eMWSaJPIAhXCtWiuH9P8q3omlrA= 718 | dependencies: 719 | async "~1.5" 720 | 721 | he@1.1.1: 722 | version "1.1.1" 723 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 724 | integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= 725 | 726 | hosted-git-info@^2.1.4: 727 | version "2.8.5" 728 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" 729 | integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== 730 | 731 | iconv-lite@^0.4.4: 732 | version "0.4.24" 733 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 734 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 735 | dependencies: 736 | safer-buffer ">= 2.1.2 < 3" 737 | 738 | ignore-walk@^3.0.1: 739 | version "3.0.3" 740 | resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" 741 | integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== 742 | dependencies: 743 | minimatch "^3.0.4" 744 | 745 | inflight@^1.0.4: 746 | version "1.0.6" 747 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 748 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 749 | dependencies: 750 | once "^1.3.0" 751 | wrappy "1" 752 | 753 | inherits@2, inherits@^2.0.1, inherits@~2.0.3: 754 | version "2.0.4" 755 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 756 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 757 | 758 | ini@~1.3.0: 759 | version "1.3.5" 760 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" 761 | integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== 762 | 763 | interpret@^1.0.0: 764 | version "1.2.0" 765 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" 766 | integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== 767 | 768 | invert-kv@^1.0.0: 769 | version "1.0.0" 770 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" 771 | integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= 772 | 773 | is-accessor-descriptor@^0.1.6: 774 | version "0.1.6" 775 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" 776 | integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= 777 | dependencies: 778 | kind-of "^3.0.2" 779 | 780 | is-accessor-descriptor@^1.0.0: 781 | version "1.0.0" 782 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 783 | integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== 784 | dependencies: 785 | kind-of "^6.0.0" 786 | 787 | is-arrayish@^0.2.1: 788 | version "0.2.1" 789 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 790 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= 791 | 792 | is-binary-path@^1.0.0: 793 | version "1.0.1" 794 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" 795 | integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= 796 | dependencies: 797 | binary-extensions "^1.0.0" 798 | 799 | is-buffer@^1.1.5: 800 | version "1.1.6" 801 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 802 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 803 | 804 | is-data-descriptor@^0.1.4: 805 | version "0.1.4" 806 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" 807 | integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= 808 | dependencies: 809 | kind-of "^3.0.2" 810 | 811 | is-data-descriptor@^1.0.0: 812 | version "1.0.0" 813 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 814 | integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== 815 | dependencies: 816 | kind-of "^6.0.0" 817 | 818 | is-descriptor@^0.1.0: 819 | version "0.1.6" 820 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 821 | integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== 822 | dependencies: 823 | is-accessor-descriptor "^0.1.6" 824 | is-data-descriptor "^0.1.4" 825 | kind-of "^5.0.0" 826 | 827 | is-descriptor@^1.0.0, is-descriptor@^1.0.2: 828 | version "1.0.2" 829 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 830 | integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== 831 | dependencies: 832 | is-accessor-descriptor "^1.0.0" 833 | is-data-descriptor "^1.0.0" 834 | kind-of "^6.0.2" 835 | 836 | is-dotfile@^1.0.0: 837 | version "1.0.3" 838 | resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" 839 | integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= 840 | 841 | is-equal-shallow@^0.1.3: 842 | version "0.1.3" 843 | resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 844 | integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= 845 | dependencies: 846 | is-primitive "^2.0.0" 847 | 848 | is-extendable@^0.1.0, is-extendable@^0.1.1: 849 | version "0.1.1" 850 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 851 | integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= 852 | 853 | is-extendable@^1.0.1: 854 | version "1.0.1" 855 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" 856 | integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== 857 | dependencies: 858 | is-plain-object "^2.0.4" 859 | 860 | is-extglob@^1.0.0: 861 | version "1.0.0" 862 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 863 | integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= 864 | 865 | is-fullwidth-code-point@^1.0.0: 866 | version "1.0.0" 867 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 868 | integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= 869 | dependencies: 870 | number-is-nan "^1.0.0" 871 | 872 | is-fullwidth-code-point@^2.0.0: 873 | version "2.0.0" 874 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 875 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 876 | 877 | is-glob@^2.0.0, is-glob@^2.0.1: 878 | version "2.0.1" 879 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 880 | integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= 881 | dependencies: 882 | is-extglob "^1.0.0" 883 | 884 | is-number@^2.1.0: 885 | version "2.1.0" 886 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 887 | integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= 888 | dependencies: 889 | kind-of "^3.0.2" 890 | 891 | is-number@^3.0.0: 892 | version "3.0.0" 893 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 894 | integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= 895 | dependencies: 896 | kind-of "^3.0.2" 897 | 898 | is-number@^4.0.0: 899 | version "4.0.0" 900 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" 901 | integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== 902 | 903 | is-plain-object@^2.0.3, is-plain-object@^2.0.4: 904 | version "2.0.4" 905 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 906 | integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== 907 | dependencies: 908 | isobject "^3.0.1" 909 | 910 | is-posix-bracket@^0.1.0: 911 | version "0.1.1" 912 | resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 913 | integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= 914 | 915 | is-primitive@^2.0.0: 916 | version "2.0.0" 917 | resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 918 | integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= 919 | 920 | is-utf8@^0.2.0: 921 | version "0.2.1" 922 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 923 | integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= 924 | 925 | is-windows@^1.0.2: 926 | version "1.0.2" 927 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 928 | integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== 929 | 930 | isarray@1.0.0, isarray@~1.0.0: 931 | version "1.0.0" 932 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 933 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 934 | 935 | isexe@^2.0.0: 936 | version "2.0.0" 937 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 938 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 939 | 940 | isobject@^2.0.0: 941 | version "2.1.0" 942 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 943 | integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= 944 | dependencies: 945 | isarray "1.0.0" 946 | 947 | isobject@^3.0.0, isobject@^3.0.1: 948 | version "3.0.1" 949 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 950 | integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= 951 | 952 | isstream@0.1.x: 953 | version "0.1.2" 954 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 955 | integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= 956 | 957 | javascript-natural-sort@0.7.1: 958 | version "0.7.1" 959 | resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" 960 | integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k= 961 | 962 | json3@3.3.2: 963 | version "3.3.2" 964 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 965 | integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= 966 | 967 | jsonfile@^2.1.0: 968 | version "2.4.0" 969 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" 970 | integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= 971 | optionalDependencies: 972 | graceful-fs "^4.1.6" 973 | 974 | keypress@^0.2.1: 975 | version "0.2.1" 976 | resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.2.1.tgz#1e80454250018dbad4c3fe94497d6e67b6269c77" 977 | integrity sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc= 978 | 979 | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: 980 | version "3.2.2" 981 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 982 | integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= 983 | dependencies: 984 | is-buffer "^1.1.5" 985 | 986 | kind-of@^4.0.0: 987 | version "4.0.0" 988 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 989 | integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= 990 | dependencies: 991 | is-buffer "^1.1.5" 992 | 993 | kind-of@^5.0.0: 994 | version "5.1.0" 995 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 996 | integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== 997 | 998 | kind-of@^6.0.0, kind-of@^6.0.2: 999 | version "6.0.2" 1000 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 1001 | integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== 1002 | 1003 | klaw@^1.0.0: 1004 | version "1.3.1" 1005 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" 1006 | integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= 1007 | optionalDependencies: 1008 | graceful-fs "^4.1.9" 1009 | 1010 | lcid@^1.0.0: 1011 | version "1.0.0" 1012 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" 1013 | integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= 1014 | dependencies: 1015 | invert-kv "^1.0.0" 1016 | 1017 | load-json-file@^1.0.0: 1018 | version "1.1.0" 1019 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 1020 | integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= 1021 | dependencies: 1022 | graceful-fs "^4.1.2" 1023 | parse-json "^2.2.0" 1024 | pify "^2.0.0" 1025 | pinkie-promise "^2.0.0" 1026 | strip-bom "^2.0.0" 1027 | 1028 | local-git-sync@^1.8.3: 1029 | version "1.8.3" 1030 | resolved "https://registry.yarnpkg.com/local-git-sync/-/local-git-sync-1.8.3.tgz#70698b7de2b2fccece626ac0f47b19f5e3cfeea2" 1031 | integrity sha1-cGmLfeKy/M7OYmrA9HsZ9ePP7qI= 1032 | dependencies: 1033 | graceful-fs "4.1.6" 1034 | shelljs "0.7.4" 1035 | 1036 | lodash._baseassign@^3.0.0: 1037 | version "3.2.0" 1038 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 1039 | integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= 1040 | dependencies: 1041 | lodash._basecopy "^3.0.0" 1042 | lodash.keys "^3.0.0" 1043 | 1044 | lodash._basecopy@^3.0.0: 1045 | version "3.0.1" 1046 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 1047 | integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= 1048 | 1049 | lodash._basecreate@^3.0.0: 1050 | version "3.0.3" 1051 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 1052 | integrity sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE= 1053 | 1054 | lodash._getnative@^3.0.0: 1055 | version "3.9.1" 1056 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 1057 | integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= 1058 | 1059 | lodash._isiterateecall@^3.0.0: 1060 | version "3.0.9" 1061 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 1062 | integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= 1063 | 1064 | lodash.create@3.1.1: 1065 | version "3.1.1" 1066 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 1067 | integrity sha1-1/KEnw29p+BGgruM1yqwIkYd6+c= 1068 | dependencies: 1069 | lodash._baseassign "^3.0.0" 1070 | lodash._basecreate "^3.0.0" 1071 | lodash._isiterateecall "^3.0.0" 1072 | 1073 | lodash.isarguments@^3.0.0: 1074 | version "3.1.0" 1075 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 1076 | integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= 1077 | 1078 | lodash.isarray@^3.0.0: 1079 | version "3.0.4" 1080 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 1081 | integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= 1082 | 1083 | lodash.keys@^3.0.0: 1084 | version "3.1.2" 1085 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 1086 | integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= 1087 | dependencies: 1088 | lodash._getnative "^3.0.0" 1089 | lodash.isarguments "^3.0.0" 1090 | lodash.isarray "^3.0.0" 1091 | 1092 | lodash@^4.16.6: 1093 | version "4.17.15" 1094 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 1095 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 1096 | 1097 | long-stack-traces@^0.1.2: 1098 | version "0.1.2" 1099 | resolved "https://registry.yarnpkg.com/long-stack-traces/-/long-stack-traces-0.1.2.tgz#6022b50ee9bbc74b5cbd7cfeeaae4165e1a546ec" 1100 | integrity sha1-YCK1Dum7x0tcvXz+6q5BZeGlRuw= 1101 | 1102 | map-cache@^0.2.2: 1103 | version "0.2.2" 1104 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 1105 | integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= 1106 | 1107 | map-visit@^1.0.0: 1108 | version "1.0.0" 1109 | resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 1110 | integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= 1111 | dependencies: 1112 | object-visit "^1.0.0" 1113 | 1114 | math-random@^1.0.1: 1115 | version "1.0.4" 1116 | resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" 1117 | integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== 1118 | 1119 | mathjs@^3.7.0: 1120 | version "3.20.2" 1121 | resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-3.20.2.tgz#3218aebde7de8cb5627c8fe3a1a216bf399ba91d" 1122 | integrity sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA== 1123 | dependencies: 1124 | complex.js "2.0.4" 1125 | decimal.js "9.0.1" 1126 | escape-latex "^1.0.0" 1127 | fraction.js "4.0.4" 1128 | javascript-natural-sort "0.7.1" 1129 | seed-random "2.2.0" 1130 | tiny-emitter "2.0.2" 1131 | typed-function "0.10.7" 1132 | 1133 | memorystream@^0.3.1: 1134 | version "0.3.1" 1135 | resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" 1136 | integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= 1137 | 1138 | micromatch@^2.1.5: 1139 | version "2.3.11" 1140 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 1141 | integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= 1142 | dependencies: 1143 | arr-diff "^2.0.0" 1144 | array-unique "^0.2.1" 1145 | braces "^1.8.2" 1146 | expand-brackets "^0.1.4" 1147 | extglob "^0.3.1" 1148 | filename-regex "^2.0.0" 1149 | is-extglob "^1.0.0" 1150 | is-glob "^2.0.1" 1151 | kind-of "^3.0.2" 1152 | normalize-path "^2.0.1" 1153 | object.omit "^2.0.0" 1154 | parse-glob "^3.0.4" 1155 | regex-cache "^0.4.2" 1156 | 1157 | micromatch@^3.1.10: 1158 | version "3.1.10" 1159 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" 1160 | integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== 1161 | dependencies: 1162 | arr-diff "^4.0.0" 1163 | array-unique "^0.3.2" 1164 | braces "^2.3.1" 1165 | define-property "^2.0.2" 1166 | extend-shallow "^3.0.2" 1167 | extglob "^2.0.4" 1168 | fragment-cache "^0.2.1" 1169 | kind-of "^6.0.2" 1170 | nanomatch "^1.2.9" 1171 | object.pick "^1.3.0" 1172 | regex-not "^1.0.0" 1173 | snapdragon "^0.8.1" 1174 | to-regex "^3.0.2" 1175 | 1176 | mime@^1.3.4: 1177 | version "1.6.0" 1178 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 1179 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 1180 | 1181 | minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: 1182 | version "3.0.4" 1183 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1184 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 1185 | dependencies: 1186 | brace-expansion "^1.1.7" 1187 | 1188 | minimist@0.0.8: 1189 | version "0.0.8" 1190 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1191 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 1192 | 1193 | minimist@^1.2.0: 1194 | version "1.2.0" 1195 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 1196 | integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= 1197 | 1198 | minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: 1199 | version "2.9.0" 1200 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" 1201 | integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== 1202 | dependencies: 1203 | safe-buffer "^5.1.2" 1204 | yallist "^3.0.0" 1205 | 1206 | minizlib@^1.2.1: 1207 | version "1.3.3" 1208 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" 1209 | integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== 1210 | dependencies: 1211 | minipass "^2.9.0" 1212 | 1213 | mixin-deep@^1.2.0: 1214 | version "1.3.2" 1215 | resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" 1216 | integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== 1217 | dependencies: 1218 | for-in "^1.0.2" 1219 | is-extendable "^1.0.1" 1220 | 1221 | mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1: 1222 | version "0.5.1" 1223 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 1224 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 1225 | dependencies: 1226 | minimist "0.0.8" 1227 | 1228 | mocha@^3.1.2: 1229 | version "3.5.3" 1230 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" 1231 | integrity sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg== 1232 | dependencies: 1233 | browser-stdout "1.3.0" 1234 | commander "2.9.0" 1235 | debug "2.6.8" 1236 | diff "3.2.0" 1237 | escape-string-regexp "1.0.5" 1238 | glob "7.1.1" 1239 | growl "1.9.2" 1240 | he "1.1.1" 1241 | json3 "3.3.2" 1242 | lodash.create "3.1.1" 1243 | mkdirp "0.5.1" 1244 | supports-color "3.1.2" 1245 | 1246 | moment-duration-format@^1.3.0: 1247 | version "1.3.0" 1248 | resolved "https://registry.yarnpkg.com/moment-duration-format/-/moment-duration-format-1.3.0.tgz#541771b5f87a049cc65540475d3ad966737d6908" 1249 | integrity sha1-VBdxtfh6BJzGVUBHXTrZZnN9aQg= 1250 | 1251 | moment@^2.16.0: 1252 | version "2.24.0" 1253 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" 1254 | integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== 1255 | 1256 | ms@2.0.0: 1257 | version "2.0.0" 1258 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 1259 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 1260 | 1261 | ms@^2.1.1: 1262 | version "2.1.2" 1263 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1264 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1265 | 1266 | nan@^2.12.1: 1267 | version "2.14.0" 1268 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" 1269 | integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== 1270 | 1271 | nanomatch@^1.2.9: 1272 | version "1.2.13" 1273 | resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" 1274 | integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== 1275 | dependencies: 1276 | arr-diff "^4.0.0" 1277 | array-unique "^0.3.2" 1278 | define-property "^2.0.2" 1279 | extend-shallow "^3.0.2" 1280 | fragment-cache "^0.2.1" 1281 | is-windows "^1.0.2" 1282 | kind-of "^6.0.2" 1283 | object.pick "^1.3.0" 1284 | regex-not "^1.0.0" 1285 | snapdragon "^0.8.1" 1286 | to-regex "^3.0.1" 1287 | 1288 | needle@^2.2.1: 1289 | version "2.4.0" 1290 | resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" 1291 | integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== 1292 | dependencies: 1293 | debug "^3.2.6" 1294 | iconv-lite "^0.4.4" 1295 | sax "^1.2.4" 1296 | 1297 | node-pre-gyp@^0.12.0: 1298 | version "0.12.0" 1299 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" 1300 | integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== 1301 | dependencies: 1302 | detect-libc "^1.0.2" 1303 | mkdirp "^0.5.1" 1304 | needle "^2.2.1" 1305 | nopt "^4.0.1" 1306 | npm-packlist "^1.1.6" 1307 | npmlog "^4.0.2" 1308 | rc "^1.2.7" 1309 | rimraf "^2.6.1" 1310 | semver "^5.3.0" 1311 | tar "^4" 1312 | 1313 | nopt@^4.0.1: 1314 | version "4.0.1" 1315 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" 1316 | integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= 1317 | dependencies: 1318 | abbrev "1" 1319 | osenv "^0.1.4" 1320 | 1321 | normalize-package-data@^2.3.2: 1322 | version "2.5.0" 1323 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" 1324 | integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== 1325 | dependencies: 1326 | hosted-git-info "^2.1.4" 1327 | resolve "^1.10.0" 1328 | semver "2 || 3 || 4 || 5" 1329 | validate-npm-package-license "^3.0.1" 1330 | 1331 | normalize-path@^2.0.0, normalize-path@^2.0.1: 1332 | version "2.1.1" 1333 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 1334 | integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= 1335 | dependencies: 1336 | remove-trailing-separator "^1.0.1" 1337 | 1338 | npm-bundled@^1.0.1: 1339 | version "1.0.6" 1340 | resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" 1341 | integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== 1342 | 1343 | npm-packlist@^1.1.6: 1344 | version "1.4.6" 1345 | resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4" 1346 | integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg== 1347 | dependencies: 1348 | ignore-walk "^3.0.1" 1349 | npm-bundled "^1.0.1" 1350 | 1351 | npmlog@^4.0.2: 1352 | version "4.1.2" 1353 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" 1354 | integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== 1355 | dependencies: 1356 | are-we-there-yet "~1.1.2" 1357 | console-control-strings "~1.1.0" 1358 | gauge "~2.7.3" 1359 | set-blocking "~2.0.0" 1360 | 1361 | number-is-nan@^1.0.0: 1362 | version "1.0.1" 1363 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1364 | integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= 1365 | 1366 | object-assign@^4.1.0: 1367 | version "4.1.1" 1368 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1369 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 1370 | 1371 | object-copy@^0.1.0: 1372 | version "0.1.0" 1373 | resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" 1374 | integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= 1375 | dependencies: 1376 | copy-descriptor "^0.1.0" 1377 | define-property "^0.2.5" 1378 | kind-of "^3.0.3" 1379 | 1380 | object-visit@^1.0.0: 1381 | version "1.0.1" 1382 | resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" 1383 | integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= 1384 | dependencies: 1385 | isobject "^3.0.0" 1386 | 1387 | object.omit@^2.0.0: 1388 | version "2.0.1" 1389 | resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" 1390 | integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= 1391 | dependencies: 1392 | for-own "^0.1.4" 1393 | is-extendable "^0.1.1" 1394 | 1395 | object.pick@^1.3.0: 1396 | version "1.3.0" 1397 | resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" 1398 | integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= 1399 | dependencies: 1400 | isobject "^3.0.1" 1401 | 1402 | once@^1.3.0: 1403 | version "1.4.0" 1404 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1405 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 1406 | dependencies: 1407 | wrappy "1" 1408 | 1409 | optional@^0.1.3: 1410 | version "0.1.4" 1411 | resolved "https://registry.yarnpkg.com/optional/-/optional-0.1.4.tgz#cdb1a9bedc737d2025f690ceeb50e049444fd5b3" 1412 | integrity sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw== 1413 | 1414 | os-homedir@^1.0.0: 1415 | version "1.0.2" 1416 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1417 | integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= 1418 | 1419 | os-locale@^1.4.0: 1420 | version "1.4.0" 1421 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" 1422 | integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= 1423 | dependencies: 1424 | lcid "^1.0.0" 1425 | 1426 | os-tmpdir@^1.0.0: 1427 | version "1.0.2" 1428 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1429 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 1430 | 1431 | osenv@^0.1.4: 1432 | version "0.1.5" 1433 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" 1434 | integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== 1435 | dependencies: 1436 | os-homedir "^1.0.0" 1437 | os-tmpdir "^1.0.0" 1438 | 1439 | parse-glob@^3.0.4: 1440 | version "3.0.4" 1441 | resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 1442 | integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= 1443 | dependencies: 1444 | glob-base "^0.3.0" 1445 | is-dotfile "^1.0.0" 1446 | is-extglob "^1.0.0" 1447 | is-glob "^2.0.0" 1448 | 1449 | parse-json@^2.2.0: 1450 | version "2.2.0" 1451 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 1452 | integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= 1453 | dependencies: 1454 | error-ex "^1.2.0" 1455 | 1456 | pascalcase@^0.1.1: 1457 | version "0.1.1" 1458 | resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" 1459 | integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= 1460 | 1461 | path-exists@^2.0.0: 1462 | version "2.1.0" 1463 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 1464 | integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= 1465 | dependencies: 1466 | pinkie-promise "^2.0.0" 1467 | 1468 | path-is-absolute@^1.0.0: 1469 | version "1.0.1" 1470 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1471 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1472 | 1473 | path-parse@^1.0.6: 1474 | version "1.0.6" 1475 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 1476 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 1477 | 1478 | path-type@^1.0.0: 1479 | version "1.1.0" 1480 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" 1481 | integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= 1482 | dependencies: 1483 | graceful-fs "^4.1.2" 1484 | pify "^2.0.0" 1485 | pinkie-promise "^2.0.0" 1486 | 1487 | pegjs@^0.10.0: 1488 | version "0.10.0" 1489 | resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" 1490 | integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0= 1491 | 1492 | pify@^2.0.0: 1493 | version "2.3.0" 1494 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 1495 | integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= 1496 | 1497 | pinkie-promise@^2.0.0: 1498 | version "2.0.1" 1499 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1500 | integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= 1501 | dependencies: 1502 | pinkie "^2.0.0" 1503 | 1504 | pinkie@^2.0.0: 1505 | version "2.0.4" 1506 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1507 | integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= 1508 | 1509 | posix-character-classes@^0.1.0: 1510 | version "0.1.1" 1511 | resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 1512 | integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= 1513 | 1514 | preserve@^0.2.0: 1515 | version "0.2.0" 1516 | resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 1517 | integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= 1518 | 1519 | process-nextick-args@~2.0.0: 1520 | version "2.0.1" 1521 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 1522 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 1523 | 1524 | promise-pauser@^1.0.0: 1525 | version "1.0.0" 1526 | resolved "https://registry.yarnpkg.com/promise-pauser/-/promise-pauser-1.0.0.tgz#db8594903bc55951446ac98ae2c2b9592febeeb0" 1527 | integrity sha1-24WUkDvFWVFEasmK4sK5WS/r7rA= 1528 | dependencies: 1529 | bluebird "^2.9.25" 1530 | 1531 | randomatic@^3.0.0: 1532 | version "3.1.1" 1533 | resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" 1534 | integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== 1535 | dependencies: 1536 | is-number "^4.0.0" 1537 | kind-of "^6.0.0" 1538 | math-random "^1.0.1" 1539 | 1540 | rc@^1.2.7: 1541 | version "1.2.8" 1542 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 1543 | integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== 1544 | dependencies: 1545 | deep-extend "^0.6.0" 1546 | ini "~1.3.0" 1547 | minimist "^1.2.0" 1548 | strip-json-comments "~2.0.1" 1549 | 1550 | read-pkg-up@^1.0.1: 1551 | version "1.0.1" 1552 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" 1553 | integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= 1554 | dependencies: 1555 | find-up "^1.0.0" 1556 | read-pkg "^1.0.0" 1557 | 1558 | read-pkg@^1.0.0: 1559 | version "1.1.0" 1560 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" 1561 | integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= 1562 | dependencies: 1563 | load-json-file "^1.0.0" 1564 | normalize-package-data "^2.3.2" 1565 | path-type "^1.0.0" 1566 | 1567 | readable-stream@^2.0.2, readable-stream@^2.0.6: 1568 | version "2.3.6" 1569 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 1570 | integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== 1571 | dependencies: 1572 | core-util-is "~1.0.0" 1573 | inherits "~2.0.3" 1574 | isarray "~1.0.0" 1575 | process-nextick-args "~2.0.0" 1576 | safe-buffer "~5.1.1" 1577 | string_decoder "~1.1.1" 1578 | util-deprecate "~1.0.1" 1579 | 1580 | readdirp@^2.0.0: 1581 | version "2.2.1" 1582 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" 1583 | integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== 1584 | dependencies: 1585 | graceful-fs "^4.1.11" 1586 | micromatch "^3.1.10" 1587 | readable-stream "^2.0.2" 1588 | 1589 | rechoir@^0.6.2: 1590 | version "0.6.2" 1591 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1592 | integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= 1593 | dependencies: 1594 | resolve "^1.1.6" 1595 | 1596 | recursive-readdir@^2.1.0: 1597 | version "2.2.2" 1598 | resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" 1599 | integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== 1600 | dependencies: 1601 | minimatch "3.0.4" 1602 | 1603 | regex-cache@^0.4.2: 1604 | version "0.4.4" 1605 | resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" 1606 | integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== 1607 | dependencies: 1608 | is-equal-shallow "^0.1.3" 1609 | 1610 | regex-not@^1.0.0, regex-not@^1.0.2: 1611 | version "1.0.2" 1612 | resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" 1613 | integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== 1614 | dependencies: 1615 | extend-shallow "^3.0.2" 1616 | safe-regex "^1.1.0" 1617 | 1618 | remove-trailing-separator@^1.0.1: 1619 | version "1.1.0" 1620 | resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 1621 | integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= 1622 | 1623 | repeat-element@^1.1.2: 1624 | version "1.1.3" 1625 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" 1626 | integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== 1627 | 1628 | repeat-string@^1.5.2, repeat-string@^1.6.1: 1629 | version "1.6.1" 1630 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 1631 | integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= 1632 | 1633 | require-directory@^2.1.1: 1634 | version "2.1.1" 1635 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 1636 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 1637 | 1638 | require-main-filename@^1.0.1: 1639 | version "1.0.1" 1640 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" 1641 | integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= 1642 | 1643 | resolve-url@^0.2.1: 1644 | version "0.2.1" 1645 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 1646 | integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= 1647 | 1648 | resolve@^1.1.6, resolve@^1.10.0: 1649 | version "1.13.1" 1650 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" 1651 | integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w== 1652 | dependencies: 1653 | path-parse "^1.0.6" 1654 | 1655 | ret@~0.1.10: 1656 | version "0.1.15" 1657 | resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 1658 | integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== 1659 | 1660 | rimraf@^2.6.1: 1661 | version "2.7.1" 1662 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 1663 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 1664 | dependencies: 1665 | glob "^7.1.3" 1666 | 1667 | safe-buffer@^5.1.2: 1668 | version "5.2.0" 1669 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 1670 | integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== 1671 | 1672 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1673 | version "5.1.2" 1674 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1675 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1676 | 1677 | safe-regex@^1.1.0: 1678 | version "1.1.0" 1679 | resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" 1680 | integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= 1681 | dependencies: 1682 | ret "~0.1.10" 1683 | 1684 | "safer-buffer@>= 2.1.2 < 3": 1685 | version "2.1.2" 1686 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1687 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1688 | 1689 | sax@^1.2.4: 1690 | version "1.2.4" 1691 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 1692 | integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== 1693 | 1694 | seed-random@2.2.0: 1695 | version "2.2.0" 1696 | resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" 1697 | integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ= 1698 | 1699 | "semver@2 || 3 || 4 || 5", semver@^5.3.0: 1700 | version "5.7.1" 1701 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 1702 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 1703 | 1704 | set-blocking@^2.0.0, set-blocking@~2.0.0: 1705 | version "2.0.0" 1706 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 1707 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 1708 | 1709 | set-value@^2.0.0, set-value@^2.0.1: 1710 | version "2.0.1" 1711 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" 1712 | integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== 1713 | dependencies: 1714 | extend-shallow "^2.0.1" 1715 | is-extendable "^0.1.1" 1716 | is-plain-object "^2.0.3" 1717 | split-string "^3.0.1" 1718 | 1719 | shelljs@0.7.4: 1720 | version "0.7.4" 1721 | resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.4.tgz#b8f04b3a74ddfafea22acf98e0be45ded53d59c8" 1722 | integrity sha1-uPBLOnTd+v6iKs+Y4L5F3tU9Wcg= 1723 | dependencies: 1724 | glob "^7.0.0" 1725 | interpret "^1.0.0" 1726 | rechoir "^0.6.2" 1727 | 1728 | signal-exit@^3.0.0: 1729 | version "3.0.2" 1730 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 1731 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 1732 | 1733 | snapdragon-node@^2.0.1: 1734 | version "2.1.1" 1735 | resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" 1736 | integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== 1737 | dependencies: 1738 | define-property "^1.0.0" 1739 | isobject "^3.0.0" 1740 | snapdragon-util "^3.0.1" 1741 | 1742 | snapdragon-util@^3.0.1: 1743 | version "3.0.1" 1744 | resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" 1745 | integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== 1746 | dependencies: 1747 | kind-of "^3.2.0" 1748 | 1749 | snapdragon@^0.8.1: 1750 | version "0.8.2" 1751 | resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" 1752 | integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== 1753 | dependencies: 1754 | base "^0.11.1" 1755 | debug "^2.2.0" 1756 | define-property "^0.2.5" 1757 | extend-shallow "^2.0.1" 1758 | map-cache "^0.2.2" 1759 | source-map "^0.5.6" 1760 | source-map-resolve "^0.5.0" 1761 | use "^3.1.0" 1762 | 1763 | source-map-resolve@^0.5.0: 1764 | version "0.5.2" 1765 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" 1766 | integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== 1767 | dependencies: 1768 | atob "^2.1.1" 1769 | decode-uri-component "^0.2.0" 1770 | resolve-url "^0.2.1" 1771 | source-map-url "^0.4.0" 1772 | urix "^0.1.0" 1773 | 1774 | source-map-url@^0.4.0: 1775 | version "0.4.0" 1776 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 1777 | integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= 1778 | 1779 | source-map@^0.5.6: 1780 | version "0.5.7" 1781 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 1782 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= 1783 | 1784 | spdx-correct@^3.0.0: 1785 | version "3.1.0" 1786 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" 1787 | integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== 1788 | dependencies: 1789 | spdx-expression-parse "^3.0.0" 1790 | spdx-license-ids "^3.0.0" 1791 | 1792 | spdx-exceptions@^2.1.0: 1793 | version "2.2.0" 1794 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" 1795 | integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== 1796 | 1797 | spdx-expression-parse@^3.0.0: 1798 | version "3.0.0" 1799 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" 1800 | integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== 1801 | dependencies: 1802 | spdx-exceptions "^2.1.0" 1803 | spdx-license-ids "^3.0.0" 1804 | 1805 | spdx-license-ids@^3.0.0: 1806 | version "3.0.5" 1807 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" 1808 | integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== 1809 | 1810 | split-string@^3.0.1, split-string@^3.0.2: 1811 | version "3.1.0" 1812 | resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" 1813 | integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== 1814 | dependencies: 1815 | extend-shallow "^3.0.0" 1816 | 1817 | stack-trace@0.0.9: 1818 | version "0.0.9" 1819 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" 1820 | integrity sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU= 1821 | 1822 | stack-trace@0.0.x: 1823 | version "0.0.10" 1824 | resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" 1825 | integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= 1826 | 1827 | static-extend@^0.1.1: 1828 | version "0.1.2" 1829 | resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" 1830 | integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= 1831 | dependencies: 1832 | define-property "^0.2.5" 1833 | object-copy "^0.1.0" 1834 | 1835 | string-width@^1.0.1, string-width@^1.0.2: 1836 | version "1.0.2" 1837 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1838 | integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= 1839 | dependencies: 1840 | code-point-at "^1.0.0" 1841 | is-fullwidth-code-point "^1.0.0" 1842 | strip-ansi "^3.0.0" 1843 | 1844 | "string-width@^1.0.2 || 2": 1845 | version "2.1.1" 1846 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 1847 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 1848 | dependencies: 1849 | is-fullwidth-code-point "^2.0.0" 1850 | strip-ansi "^4.0.0" 1851 | 1852 | string_decoder@~1.1.1: 1853 | version "1.1.1" 1854 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1855 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 1856 | dependencies: 1857 | safe-buffer "~5.1.0" 1858 | 1859 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 1860 | version "3.0.1" 1861 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1862 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 1863 | dependencies: 1864 | ansi-regex "^2.0.0" 1865 | 1866 | strip-ansi@^4.0.0: 1867 | version "4.0.0" 1868 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1869 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 1870 | dependencies: 1871 | ansi-regex "^3.0.0" 1872 | 1873 | strip-bom@^2.0.0: 1874 | version "2.0.0" 1875 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 1876 | integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= 1877 | dependencies: 1878 | is-utf8 "^0.2.0" 1879 | 1880 | strip-json-comments@~2.0.1: 1881 | version "2.0.1" 1882 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1883 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 1884 | 1885 | supports-color@3.1.2: 1886 | version "3.1.2" 1887 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 1888 | integrity sha1-cqJiiU2dQIuVbKBf83su2KbiotU= 1889 | dependencies: 1890 | has-flag "^1.0.0" 1891 | 1892 | tar@^4: 1893 | version "4.4.13" 1894 | resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" 1895 | integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== 1896 | dependencies: 1897 | chownr "^1.1.1" 1898 | fs-minipass "^1.2.5" 1899 | minipass "^2.8.6" 1900 | minizlib "^1.2.1" 1901 | mkdirp "^0.5.0" 1902 | safe-buffer "^5.1.2" 1903 | yallist "^3.0.3" 1904 | 1905 | tiny-emitter@2.0.2: 1906 | version "2.0.2" 1907 | resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" 1908 | integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow== 1909 | 1910 | to-object-path@^0.3.0: 1911 | version "0.3.0" 1912 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" 1913 | integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= 1914 | dependencies: 1915 | kind-of "^3.0.2" 1916 | 1917 | to-regex-range@^2.1.0: 1918 | version "2.1.1" 1919 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" 1920 | integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= 1921 | dependencies: 1922 | is-number "^3.0.0" 1923 | repeat-string "^1.6.1" 1924 | 1925 | to-regex@^3.0.1, to-regex@^3.0.2: 1926 | version "3.0.2" 1927 | resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" 1928 | integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== 1929 | dependencies: 1930 | define-property "^2.0.2" 1931 | extend-shallow "^3.0.2" 1932 | regex-not "^1.0.2" 1933 | safe-regex "^1.1.0" 1934 | 1935 | type-detect@0.1.1: 1936 | version "0.1.1" 1937 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" 1938 | integrity sha1-C6XsKohWQORw6k6FBZcZANrFiCI= 1939 | 1940 | type-detect@^1.0.0: 1941 | version "1.0.0" 1942 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" 1943 | integrity sha1-diIXzAbbJY7EiQihKY6LlRIejqI= 1944 | 1945 | typed-function@0.10.7: 1946 | version "0.10.7" 1947 | resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-0.10.7.tgz#f702af7d77a64b61abf86799ff2d74266ebc4477" 1948 | integrity sha512-3mlZ5AwRMbLvUKkc8a1TI4RUJUS2H27pmD5q0lHRObgsoWzhDAX01yg82kwSP1FUw922/4Y9ZliIEh0qJZcz+g== 1949 | 1950 | union-value@^1.0.0: 1951 | version "1.0.1" 1952 | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" 1953 | integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== 1954 | dependencies: 1955 | arr-union "^3.1.0" 1956 | get-value "^2.0.6" 1957 | is-extendable "^0.1.1" 1958 | set-value "^2.0.1" 1959 | 1960 | unset-value@^1.0.0: 1961 | version "1.0.0" 1962 | resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" 1963 | integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= 1964 | dependencies: 1965 | has-value "^0.3.1" 1966 | isobject "^3.0.0" 1967 | 1968 | urix@^0.1.0: 1969 | version "0.1.0" 1970 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 1971 | integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= 1972 | 1973 | use@^3.1.0: 1974 | version "3.1.1" 1975 | resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" 1976 | integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== 1977 | 1978 | util-deprecate@~1.0.1: 1979 | version "1.0.2" 1980 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1981 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1982 | 1983 | validate-npm-package-license@^3.0.1: 1984 | version "3.0.4" 1985 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" 1986 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== 1987 | dependencies: 1988 | spdx-correct "^3.0.0" 1989 | spdx-expression-parse "^3.0.0" 1990 | 1991 | which-module@^1.0.0: 1992 | version "1.0.0" 1993 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" 1994 | integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= 1995 | 1996 | which@^1.1.1: 1997 | version "1.3.1" 1998 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1999 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 2000 | dependencies: 2001 | isexe "^2.0.0" 2002 | 2003 | wide-align@^1.1.0: 2004 | version "1.1.3" 2005 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 2006 | integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== 2007 | dependencies: 2008 | string-width "^1.0.2 || 2" 2009 | 2010 | winston@^2.3.0: 2011 | version "2.4.4" 2012 | resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.4.tgz#a01e4d1d0a103cf4eada6fc1f886b3110d71c34b" 2013 | integrity sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q== 2014 | dependencies: 2015 | async "~1.0.0" 2016 | colors "1.0.x" 2017 | cycle "1.0.x" 2018 | eyes "0.1.x" 2019 | isstream "0.1.x" 2020 | stack-trace "0.0.x" 2021 | 2022 | wrap-ansi@^2.0.0: 2023 | version "2.1.0" 2024 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" 2025 | integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= 2026 | dependencies: 2027 | string-width "^1.0.1" 2028 | strip-ansi "^3.0.1" 2029 | 2030 | wrappy@1: 2031 | version "1.0.2" 2032 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 2033 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 2034 | 2035 | y18n@^3.2.1: 2036 | version "3.2.1" 2037 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" 2038 | integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= 2039 | 2040 | yallist@^3.0.0, yallist@^3.0.3: 2041 | version "3.1.1" 2042 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 2043 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== 2044 | 2045 | yargs-parser@^4.2.0: 2046 | version "4.2.1" 2047 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" 2048 | integrity sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw= 2049 | dependencies: 2050 | camelcase "^3.0.0" 2051 | 2052 | yargs@^6.3.0: 2053 | version "6.6.0" 2054 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" 2055 | integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg= 2056 | dependencies: 2057 | camelcase "^3.0.0" 2058 | cliui "^3.2.0" 2059 | decamelize "^1.1.1" 2060 | get-caller-file "^1.0.1" 2061 | os-locale "^1.4.0" 2062 | read-pkg-up "^1.0.1" 2063 | require-directory "^2.1.1" 2064 | require-main-filename "^1.0.1" 2065 | set-blocking "^2.0.0" 2066 | string-width "^1.0.2" 2067 | which-module "^1.0.0" 2068 | y18n "^3.2.1" 2069 | yargs-parser "^4.2.0" 2070 | --------------------------------------------------------------------------------