diff --git a/.appveyor.yml b/.appveyor.yml index 817effc..75001db 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,16 +2,17 @@ install: - sc config wuauserv start= auto - net start wuauserv - - cinst php composer ffmpeg phantomjs + - cinst php --version 7.1.28 + - cinst composer ffmpeg phantomjs - refreshenv - - copy C:\tools\php72\php.ini-development C:\tools\php72\php.ini - - echo extension=C:\tools\php72\ext\php_gmp.dll >> C:\tools\php72\php.ini - - echo extension=C:\tools\php72\ext\php_gettext.dll >> C:\tools\php72\php.ini - - echo extension=C:\tools\php72\ext\php_intl.dll >> C:\tools\php72\php.ini - - echo extension=C:\tools\php72\ext\php_openssl.dll >> C:\tools\php72\php.ini - - echo extension=C:\tools\php72\ext\php_mbstring.dll >> C:\tools\php72\php.ini + - copy C:\tools\php71\php.ini-development C:\tools\php71\php.ini + - echo extension=C:\tools\php71\ext\php_gmp.dll >> C:\tools\php71\php.ini + - echo extension=C:\tools\php71\ext\php_gettext.dll >> C:\tools\php71\php.ini + - echo extension=C:\tools\php71\ext\php_intl.dll >> C:\tools\php71\php.ini + - echo extension=C:\tools\php71\ext\php_openssl.dll >> C:\tools\php71\php.ini + - echo extension=C:\tools\php71\ext\php_mbstring.dll >> C:\tools\php71\php.ini - composer install --no-dev - - composer global require phpunit/phpunit + - composer global require phpunit/phpunit:^6.0 - C:\Python36\python.exe -m pip install youtube-dl test_script: diff --git a/Gruntfile.js b/Gruntfile.js index 6850a24..86d32e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -33,6 +33,19 @@ module.exports = function (grunt) { src: ['tests/*.php'] } }, + phpstan: { + options: { + level: 'max', + bin: 'vendor/bin/phpstan', + config: 'phpstan.neon' + }, + php: { + src: ['*.php', 'classes/*.php', 'controllers/*.php'] + }, + tests: { + src: ['tests/*.php'] + } + }, jslint: { js: { src: ['js/*.js'] @@ -57,7 +70,7 @@ module.exports = function (grunt) { options: { archive: 'alltube-<%= githash.main.tag %>.zip' }, - src: ['*.php', 'config/*', '!config/config.yml', 'dist/**', '.htaccess', 'img/**', 'LICENSE', 'README.md', 'robots.txt', 'resources/sitemap.xml', 'resources/manifest.json', 'templates/**', 'templates_c/', 'vendor/**', 'classes/**', 'controllers/**', 'bower_components/**', 'i18n/**', '!vendor/ffmpeg/**', '!vendor/bin/ffmpeg', '!vendor/anam/phantomjs-linux-x86-binary/**', '!vendor/bin/phantomjs', '!vendor/phpunit/**', '!vendor/squizlabs/**', '!vendor/rinvex/country/resources/geodata/*.json', '!vendor/rinvex/country/resources/flags/*.svg', 'node_modules/open-sans-fontface/fonts/**'] + src: ['*.php', 'config/*', '!config/config.yml', 'dist/**', '.htaccess', 'img/**', 'LICENSE', 'README.md', 'robots.txt', 'resources/sitemap.xml', 'resources/manifest.json', 'templates/**', 'templates_c/', 'vendor/**', 'classes/**', 'controllers/**', 'bower_components/**', 'i18n/**', '!vendor/ffmpeg/**', '!vendor/bin/ffmpeg', '!vendor/anam/phantomjs-linux-x86-binary/**', '!vendor/bin/phantomjs', '!vendor/phpunit/**', '!vendor/squizlabs/**', '!vendor/rinvex/countries/resources/geodata/*.json', '!vendor/countries/country/resources/flags/*.svg', 'node_modules/open-sans-fontface/fonts/**'] } }, phpdocumentor: { @@ -125,9 +138,10 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-potomo'); grunt.loadNpmTasks('grunt-contrib-csslint'); grunt.loadNpmTasks('grunt-markdownlint'); + grunt.loadNpmTasks('grunt-phpstan'); grunt.registerTask('default', ['cssmin', 'potomo']); - grunt.registerTask('lint', ['csslint', 'fixpack', 'jsonlint', 'markdownlint', 'phpcs']); + grunt.registerTask('lint', ['csslint', 'fixpack', 'jsonlint', 'markdownlint', 'phpcs', 'phpstan']); grunt.registerTask('test', ['phpunit']); grunt.registerTask('doc', ['phpdocumentor']); grunt.registerTask('release', ['default', 'githash', 'compress']); diff --git a/README.md b/README.md index db3d170..9c99f51 100644 --- a/README.md +++ b/README.md @@ -162,19 +162,17 @@ You can then use it in your PHP code: ```php use Alltube\Config; -use Alltube\VideoDownload; +use Alltube\Video; require_once __DIR__.'/vendor/autoload.php'; -$downloader = new VideoDownload( - new Config( - [ - 'youtubedl' => '/usr/local/bin/youtube-dl', - ] - ) +Config::setOptions( + [ + 'youtubedl' => '/usr/local/bin/youtube-dl', + ] ); - -$downloader->getURL('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); +$video = new Video('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); +$video->getUrl(); ``` The library documentation is available on [alltube.surge.sh](https://alltube.surge.sh/classes/Alltube.VideoDownload.html). diff --git a/classes/Config.php b/classes/Config.php index cf55404..1e8e9a1 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -16,7 +16,7 @@ class Config /** * Singleton instance. * - * @var Config + * @var Config|null */ private static $instance; @@ -129,18 +129,50 @@ class Config /** * Config constructor. * - * @param array $options Options (see `config/config.example.yml` for available options) + * @param array $options Options */ - public function __construct(array $options) + private function __construct(array $options = []) { - if (isset($options) && is_array($options)) { - foreach ($options as $option => $value) { - if (isset($this->$option) && isset($value)) { - $this->$option = $value; - } + $this->applyOptions($options); + $this->getEnv(); + $this->validateOptions(); + } + + /** + * Throw an exception if some of the options are invalid. + * + * @throws Exception If youtube-dl is missing + * @throws Exception If Python is missing + * + * @return void + */ + private function validateOptions() + { + /* + We don't translate these exceptions because they usually occur before Slim can catch them + so they will go to the logs. + */ + if (!is_file($this->youtubedl)) { + throw new Exception("Can't find youtube-dl at ".$this->youtubedl); + } elseif (!Video::checkCommand([$this->python, '--version'])) { + throw new Exception("Can't find Python at ".$this->python); + } + } + + /** + * Apply the provided options. + * + * @param array $options Options + * + * @return void + */ + private function applyOptions(array $options) + { + foreach ($options as $option => $value) { + if (isset($this->$option) && isset($value)) { + $this->$option = $value; } } - $this->getEnv(); } /** @@ -161,34 +193,51 @@ class Config } /** - * Get Config singleton instance from YAML config file. - * - * @param string $yamlfile YAML config file name + * Get Config singleton instance. * * @return Config */ - public static function getInstance($yamlfile = 'config/config.yml') + public static function getInstance() { - $yamlPath = __DIR__.'/../'.$yamlfile; - if (is_null(self::$instance) || self::$instance->file != $yamlfile) { - if (is_file($yamlfile)) { - $options = Yaml::parse(file_get_contents($yamlPath)); - } elseif ($yamlfile == 'config/config.yml' || empty($yamlfile)) { - /* - Allow for the default file to be missing in order to - not surprise users that did not create a config file - */ - $options = []; - } else { - throw new Exception("Can't find config file at ".$yamlPath); - } - self::$instance = new self($options); - self::$instance->file = $yamlfile; + if (!isset(self::$instance)) { + self::$instance = new self(); } return self::$instance; } + /** + * Set options from a YAML file. + * + * @param string $file Path to the YAML file + */ + public static function setFile($file) + { + if (is_file($file)) { + $options = Yaml::parse(file_get_contents($file)); + self::$instance = new self($options); + } else { + throw new Exception("Can't find config file at ".$file); + } + } + + /** + * Manually set some options. + * + * @param array $options Options (see `config/config.example.yml` for available options) + * @param bool $update True to update an existing instance + */ + public static function setOptions(array $options, $update = true) + { + if ($update) { + $config = self::getInstance(); + $config->applyOptions($options); + $config->validateOptions(); + } else { + self::$instance = new self($options); + } + } + /** * Destroy singleton instance. * diff --git a/classes/LocaleManager.php b/classes/LocaleManager.php index cbc1ddd..c2bbc14 100644 --- a/classes/LocaleManager.php +++ b/classes/LocaleManager.php @@ -6,7 +6,6 @@ namespace Alltube; use Aura\Session\Segment; -use Aura\Session\SessionFactory; use Symfony\Component\Process\Process; /** @@ -24,7 +23,7 @@ class LocaleManager /** * Current locale. * - * @var Locale + * @var Locale|null */ private $curLocale; @@ -37,13 +36,10 @@ class LocaleManager /** * LocaleManager constructor. - * - * @param array $cookies Cookie array */ - public function __construct(array $cookies = []) + public function __construct() { - $session_factory = new SessionFactory(); - $session = $session_factory->newInstance($cookies); + $session = SessionManager::getSession(); $this->sessionSegment = $session->getSegment(self::class); $cookieLocale = $this->sessionSegment->get('locale'); if (isset($cookieLocale)) { @@ -78,7 +74,7 @@ class LocaleManager /** * Get the current locale. * - * @return Locale + * @return Locale|null */ public function getLocale() { diff --git a/classes/LocaleMiddleware.php b/classes/LocaleMiddleware.php index 51dfe6e..9aeb81e 100644 --- a/classes/LocaleMiddleware.php +++ b/classes/LocaleMiddleware.php @@ -37,7 +37,7 @@ class LocaleMiddleware * * @param array $proposedLocale Locale array created by AcceptLanguage::parse() * - * @return string Locale name if chosen, nothing otherwise + * @return Locale Locale if chosen, nothing otherwise */ public function testLocale(array $proposedLocale) { @@ -65,7 +65,7 @@ class LocaleMiddleware { $headers = $request->getHeader('Accept-Language'); $curLocale = $this->localeManager->getLocale(); - if (!isset($curLocale)) { + if (is_null($curLocale)) { if (isset($headers[0])) { $this->localeManager->setLocale( AcceptLanguage::detect([$this, 'testLocale'], new Locale('en_US'), $headers[0]) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php deleted file mode 100644 index 3c45a04..0000000 --- a/classes/PlaylistArchiveStream.php +++ /dev/null @@ -1,209 +0,0 @@ -client = new Client(); - $this->download = new VideoDownload($config); - } - - /** - * Add data to the archive. - * - * @param string $data Data - * - * @return void - */ - protected function send($data) - { - $pos = ftell($this->buffer); - fwrite($this->buffer, $data); - if ($pos !== false) { - fseek($this->buffer, $pos); - } - } - - /** - * Called when fopen() is used on the stream. - * - * @param string $path Playlist path (should be playlist://url1;url2;.../format) - * - * @return bool - */ - public function stream_open($path) - { - $this->format = ltrim(parse_url($path, PHP_URL_PATH), '/'); - $buffer = fopen('php://temp', 'r+'); - if ($buffer !== false) { - $this->buffer = $buffer; - } - foreach (explode(';', parse_url($path, PHP_URL_HOST)) as $url) { - $this->files[] = [ - 'url' => urldecode($url), - 'headersSent' => false, - 'complete' => false, - 'stream' => null, - ]; - } - - return true; - } - - /** - * Called when fwrite() is used on the stream. - * - * @return int - */ - public function stream_write() - { - //We don't support writing to a stream - return 0; - } - - /** - * Called when fstat() is used on the stream. - * - * @return array - */ - public function stream_stat() - { - //We need this so Slim won't try to get the size of the stream - return [ - 'mode' => 0010000, - ]; - } - - /** - * Called when ftell() is used on the stream. - * - * @return int|false - */ - public function stream_tell() - { - return ftell($this->buffer); - } - - /** - * Called when fseek() is used on the stream. - * - * @param int $offset Offset - * - * @return bool - */ - public function stream_seek($offset) - { - return fseek($this->buffer, $offset) == 0; - } - - /** - * Called when feof() is used on the stream. - * - * @return bool - */ - public function stream_eof() - { - foreach ($this->files as $file) { - if (!$file['complete']) { - return false; - } - } - - return true; - } - - /** - * Called when fread() is used on the stream. - * - * @param int $count Number of bytes to read - * - * @return string|false - */ - public function stream_read($count) - { - if (!$this->files[$this->curFile]['headersSent']) { - $urls = $this->download->getUrl($this->files[$this->curFile]['url'], $this->format); - $response = $this->client->request('GET', $urls[0], ['stream' => true]); - - $contentLengthHeaders = $response->getHeader('Content-Length'); - $this->init_file_stream_transfer( - $this->download->getFilename($this->files[$this->curFile]['url'], $this->format), - $contentLengthHeaders[0] - ); - - $this->files[$this->curFile]['headersSent'] = true; - $this->files[$this->curFile]['stream'] = $response->getBody(); - } elseif (!$this->files[$this->curFile]['stream']->eof()) { - $this->stream_file_part($this->files[$this->curFile]['stream']->read($count)); - } elseif (!$this->files[$this->curFile]['complete']) { - $this->complete_file_stream(); - $this->files[$this->curFile]['complete'] = true; - } elseif (isset($this->files[$this->curFile])) { - $this->curFile += 1; - } - - return fread($this->buffer, $count); - } -} diff --git a/classes/SessionManager.php b/classes/SessionManager.php new file mode 100644 index 0000000..0ecd586 --- /dev/null +++ b/classes/SessionManager.php @@ -0,0 +1,37 @@ +newInstance($_COOKIE); + } + + return self::$session; + } +} diff --git a/classes/VideoDownload.php b/classes/Video.php similarity index 58% rename from classes/VideoDownload.php rename to classes/Video.php index 0f3c462..127102c 100644 --- a/classes/VideoDownload.php +++ b/classes/Video.php @@ -5,14 +5,30 @@ namespace Alltube; +use Alltube\Exception\EmptyUrlException; +use Alltube\Exception\PasswordException; use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\Psr7\Response; use stdClass; use Symfony\Component\Process\Process; /** * Extract info about videos. + * + * Due to the way youtube-dl behaves, this class can also contain information about a playlist. + * + * @property-read string $title Title + * @property-read string $protocol Network protocol (HTTP, RTMP, etc.) + * @property-read string $url File URL + * @property-read string $ext File extension + * @property-read string $extractor_key youtube-dl extractor class used + * @property-read array $entries List of videos (if the object contains information about a playlist) + * @property-read array $rtmp_conn + * @property-read string|null $_type Object type (usually "playlist" or null) + * @property-read stdClass $downloader_options */ -class VideoDownload +class Video { /** * Config instance. @@ -21,30 +37,54 @@ class VideoDownload */ private $config; + /** + * URL of the page containing the video. + * + * @var string + */ + private $webpageUrl; + + /** + * Requested video format. + * + * @var string + */ + private $requestedFormat; + + /** + * Password. + * + * @var string|null + */ + private $password; + + /** + * JSON object returned by youtube-dl. + * + * @var stdClass + */ + private $json; + + /** + * URLs of the video files. + * + * @var array + */ + private $urls; + /** * VideoDownload constructor. * - * @param Config $config Config instance. - * - * @throws Exception If youtube-dl is missing - * @throws Exception If Python is missing + * @param string $webpageUrl URL of the page containing the video + * @param string $requestedFormat Requested video format + * @param string $password Password */ - public function __construct(Config $config = null) + public function __construct($webpageUrl, $requestedFormat = 'best', $password = null) { - if (isset($config)) { - $this->config = $config; - } else { - $this->config = Config::getInstance(); - } - /* - We don't translate these exceptions because they always occur before Slim can catch them - so they will always go to the logs. - */ - if (!is_file($this->config->youtubedl)) { - throw new Exception("Can't find youtube-dl at ".$this->config->youtubedl); - } elseif (!$this->checkCommand([$this->config->python, '--version'])) { - throw new Exception("Can't find Python at ".$this->config->python); - } + $this->webpageUrl = $webpageUrl; + $this->requestedFormat = $requestedFormat; + $this->password = $password; + $this->config = Config::getInstance(); } /** @@ -54,12 +94,14 @@ class VideoDownload * * @return Process */ - private function getProcess(array $arguments) + private static function getProcess(array $arguments) { + $config = Config::getInstance(); + return new Process( array_merge( - [$this->config->python, $this->config->youtubedl], - $this->config->params, + [$config->python, $config->youtubedl], + $config->params, $arguments ) ); @@ -70,42 +112,29 @@ class VideoDownload * * @return string[] Extractors * */ - public function listExtractors() + public static function getExtractors() { - return explode("\n", trim($this->getProp(null, null, 'list-extractors'))); + return explode("\n", trim(self::callYoutubedl(['--list-extractors']))); } /** - * Get a property from youtube-dl. + * Call youtube-dl. * - * @param string $url URL to parse - * @param string $format Format - * @param string $prop Property - * @param string $password Video password + * @param array $arguments Arguments * * @throws PasswordException If the video is protected by a password and no password was specified * @throws Exception If the password is wrong * @throws Exception If youtube-dl returns an error * - * @return string + * @return string Result */ - private function getProp($url, $format = null, $prop = 'dump-json', $password = null) + private static function callYoutubedl(array $arguments) { - $arguments = [ - '--'.$prop, - $url, - ]; - if (isset($format)) { - $arguments[] = '-f '.$format; - } - if (isset($password)) { - $arguments[] = '--video-password'; - $arguments[] = $password; - } + $config = Config::getInstance(); - $process = $this->getProcess($arguments); + $process = self::getProcess($arguments); //This is needed by the openload extractor because it runs PhantomJS - $process->setEnv(['PATH'=>$this->config->phantomjsDir]); + $process->setEnv(['PATH'=>$config->phantomjsDir]); $process->inheritEnvironmentVariables(); $process->run(); if (!$process->isSuccessful()) { @@ -123,18 +152,70 @@ class VideoDownload } } + /** + * Get a property from youtube-dl. + * + * @param string $prop Property + * + * @return string + */ + private function getProp($prop = 'dump-json') + { + $arguments = ['--'.$prop]; + + if (isset($this->webpageUrl)) { + $arguments[] = $this->webpageUrl; + } + if (isset($this->requestedFormat)) { + $arguments[] = '-f'; + $arguments[] = $this->requestedFormat; + } + if (isset($this->password)) { + $arguments[] = '--video-password'; + $arguments[] = $this->password; + } + + return $this::callYoutubedl($arguments); + } + /** * Get all information about a video. * - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password - * - * @return object Decoded JSON + * @return stdClass Decoded JSON * */ - public function getJSON($url, $format = null, $password = null) + public function getJson() { - return json_decode($this->getProp($url, $format, 'dump-single-json', $password)); + if (!isset($this->json)) { + $this->json = json_decode($this->getProp('dump-single-json')); + } + + return $this->json; + } + + /** + * Magic method to get a property from the JSON object returned by youtube-dl. + * + * @param string $name Property + * + * @return mixed + */ + public function __get($name) + { + if (isset($this->$name)) { + return $this->getJson()->$name; + } + } + + /** + * Magic method to check if the JSON object returned by youtube-dl has a property. + * + * @param string $name Property + * + * @return bool + */ + public function __isset($name) + { + return isset($this->getJson()->$name); } /** @@ -144,52 +225,44 @@ class VideoDownload * But it can return two URLs when multiple formats are specified * (eg. bestvideo+bestaudio). * - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password - * * @return string[] URLs of video * */ - public function getURL($url, $format = null, $password = null) + public function getUrl() { - $urls = explode("\n", $this->getProp($url, $format, 'get-url', $password)); + // Cache the URLs. + if (!isset($this->urls)) { + $this->urls = explode("\n", $this->getProp('get-url')); - if (empty($urls[0])) { - throw new EmptyUrlException(_('youtube-dl returned an empty URL.')); + if (empty($this->urls[0])) { + throw new EmptyUrlException(_('youtube-dl returned an empty URL.')); + } } - return $urls; + return $this->urls; } /** * Get filename of video file from URL of page. * - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password - * * @return string Filename of extracted video * */ - public function getFilename($url, $format = null, $password = null) + public function getFilename() { - return trim($this->getProp($url, $format, 'get-filename', $password)); + return trim($this->getProp('get-filename')); } /** * Get filename of video with the specified extension. * * @param string $extension New file extension - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password * * @return string Filename of extracted video with specified extension */ - public function getFileNameWithExtension($extension, $url, $format = null, $password = null) + public function getFileNameWithExtension($extension) { return html_entity_decode( pathinfo( - $this->getFilename($url, $format, $password), + $this->getFilename(), PATHINFO_FILENAME ).'.'.$extension, ENT_COMPAT, @@ -197,32 +270,16 @@ class VideoDownload ); } - /** - * Get filename of audio from URL of page. - * - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password - * - * @return string Filename of converted audio file - * */ - public function getAudioFilename($url, $format = null, $password = null) - { - return $this->getFileNameWithExtension('mp3', $url, $format, $password); - } - /** * Return arguments used to run rtmp for a specific video. * - * @param object $video Video object returned by youtube-dl - * * @return array Arguments */ - private function getRtmpArguments(stdClass $video) + private function getRtmpArguments() { $arguments = []; - if ($video->protocol == 'rtmp') { + if ($this->protocol == 'rtmp') { foreach ([ 'url' => '-rtmp_tcurl', 'webpage_url' => '-rtmp_pageurl', @@ -231,14 +288,14 @@ class VideoDownload 'play_path' => '-rtmp_playpath', 'app' => '-rtmp_app', ] as $property => $option) { - if (isset($video->{$property})) { + if (isset($this->{$property})) { $arguments[] = $option; - $arguments[] = $video->{$property}; + $arguments[] = $this->{$property}; } } - if (isset($video->rtmp_conn)) { - foreach ($video->rtmp_conn as $conn) { + if (isset($this->rtmp_conn)) { + foreach ($this->rtmp_conn as $conn) { $arguments[] = '-rtmp_conn'; $arguments[] = $conn; } @@ -255,7 +312,7 @@ class VideoDownload * * @return bool False if the command returns an error, true otherwise */ - private function checkCommand(array $command) + public static function checkCommand(array $command) { $process = new Process($command); $process->run(); @@ -266,7 +323,6 @@ class VideoDownload /** * Get a process that runs avconv in order to convert a video. * - * @param object $video Video object returned by youtube-dl * @param int $audioBitrate Audio bitrate of the converted file * @param string $filetype Filetype of the converted file * @param bool $audioOnly True to return an audio-only file @@ -278,7 +334,6 @@ class VideoDownload * @return Process Process */ private function getAvconvProcess( - stdClass $video, $audioBitrate, $filetype = 'mp3', $audioOnly = true, @@ -312,14 +367,16 @@ class VideoDownload $afterArguments[] = $to; } + $urls = $this->getUrl(); + $arguments = array_merge( [ $this->config->avconv, '-v', $this->config->avconvVerbosity, ], - $this->getRtmpArguments($video), + $this->getRtmpArguments(), [ - '-i', $video->url, + '-i', $urls[0], '-f', $filetype, '-b:a', $audioBitrate.'k', ], @@ -328,11 +385,10 @@ class VideoDownload 'pipe:1', ] ); - if ($video->url != '-') { - //Vimeo needs a correct user-agent - $arguments[] = '-user_agent'; - $arguments[] = $this->getProp(null, null, 'dump-user-agent'); - } + + //Vimeo needs a correct user-agent + $arguments[] = '-user_agent'; + $arguments[] = $this->getProp('dump-user-agent'); return new Process($arguments); } @@ -340,34 +396,29 @@ class VideoDownload /** * Get audio stream of converted video. * - * @param string $url URL of page - * @param string $format Format to use for the video - * @param string $password Video password - * @param string $from Start the conversion at this time - * @param string $to End the conversion at this time + * @param string $from Start the conversion at this time + * @param string $to End the conversion at this time * - * @throws Exception If your try to convert and M3U8 video + * @throws Exception If your try to convert an M3U8 video * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getAudioStream($url, $format, $password = null, $from = null, $to = null) + public function getAudioStream($from = null, $to = null) { - $video = $this->getJSON($url, $format, $password); - - if (isset($video->_type) && $video->_type == 'playlist') { + if (isset($this->_type) && $this->_type == 'playlist') { throw new Exception(_('Conversion of playlists is not supported.')); } - if (isset($video->protocol)) { - if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) { + if (isset($this->protocol)) { + if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { throw new Exception(_('Conversion of M3U8 files is not supported.')); - } elseif ($video->protocol == 'http_dash_segments') { + } elseif ($this->protocol == 'http_dash_segments') { throw new Exception(_('Conversion of DASH segments is not supported.')); } } - $avconvProc = $this->getAvconvProcess($video, $this->config->audioBitrate, 'mp3', true, $from, $to); + $avconvProc = $this->getAvconvProcess($this->config->audioBitrate, 'mp3', true, $from, $to); $stream = popen($avconvProc->getCommandLine(), 'r'); @@ -381,25 +432,25 @@ class VideoDownload /** * Get video stream from an M3U playlist. * - * @param stdClass $video Video object returned by getJSON - * * @throws Exception If avconv/ffmpeg is missing * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getM3uStream(stdClass $video) + public function getM3uStream() { if (!$this->checkCommand([$this->config->avconv, '-version'])) { throw new Exception(_('Can\'t find avconv or ffmpeg at ').$this->config->avconv.'.'); } + $urls = $this->getUrl(); + $process = new Process( [ $this->config->avconv, '-v', $this->config->avconvVerbosity, - '-i', $video->url, - '-f', $video->ext, + '-i', $urls[0], + '-f', $this->ext, '-c', 'copy', '-bsf:a', 'aac_adtstoasc', '-movflags', 'frag_keyframe+empty_moov', @@ -418,14 +469,18 @@ class VideoDownload /** * Get an avconv stream to remux audio and video. * - * @param array $urls URLs of the video ($urls[0]) and audio ($urls[1]) files - * * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getRemuxStream(array $urls) + public function getRemuxStream() { + $urls = $this->getUrl(); + + if (!isset($urls[0]) || !isset($urls[1])) { + throw new Exception(_('This video does not have two URLs.')); + } + $process = new Process( [ $this->config->avconv, @@ -451,24 +506,24 @@ class VideoDownload /** * Get video stream from an RTMP video. * - * @param stdClass $video Video object returned by getJSON - * * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getRtmpStream(stdClass $video) + public function getRtmpStream() { + $urls = $this->getUrl(); + $process = new Process( array_merge( [ $this->config->avconv, '-v', $this->config->avconvVerbosity, ], - $this->getRtmpArguments($video), + $this->getRtmpArguments(), [ - '-i', $video->url, - '-f', $video->ext, + '-i', $urls[0], + '-f', $this->ext, 'pipe:1', ] ) @@ -481,52 +536,24 @@ class VideoDownload return $stream; } - /** - * Get a Tar stream containing every video in the playlist piped through the server. - * - * @param object $video Video object returned by youtube-dl - * @param string $format Requested format - * - * @throws Exception If the popen stream was not created correctly - * - * @return resource - */ - public function getPlaylistArchiveStream(stdClass $video, $format) - { - $playlistItems = []; - foreach ($video->entries as $entry) { - $playlistItems[] = urlencode($entry->url); - } - $stream = fopen('playlist://'.implode(';', $playlistItems).'/'.$format, 'r'); - if (!is_resource($stream)) { - throw new Exception(_('Could not open fopen stream.')); - } - - return $stream; - } - /** * Get the stream of a converted video. * - * @param string $url URL of page - * @param string $format Source format to use for the conversion * @param int $audioBitrate Audio bitrate of the converted file * @param string $filetype Filetype of the converted file - * @param string $password Video password * * @throws Exception If your try to convert and M3U8 video * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getConvertedStream($url, $format, $audioBitrate, $filetype, $password = null) + public function getConvertedStream($audioBitrate, $filetype) { - $video = $this->getJSON($url, $format, $password); - if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) { + if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { throw new Exception(_('Conversion of M3U8 files is not supported.')); } - $avconvProc = $this->getAvconvProcess($video, $audioBitrate, $filetype, false); + $avconvProc = $this->getAvconvProcess($audioBitrate, $filetype, false); $stream = popen($avconvProc->getCommandLine(), 'r'); @@ -536,4 +563,31 @@ class VideoDownload return $stream; } + + /** + * Get the same video but with another format. + * + * @param string $format New format + * + * @return Video + */ + public function withFormat($format) + { + return new self($this->webpageUrl, $format, $this->password); + } + + /** + * Get a HTTP response containing the video. + * + * @param array $headers HTTP headers of the request + * + * @return Response + */ + public function getHttpResponse(array $headers = []) + { + $client = new Client(); + $urls = $this->getUrl(); + + return $client->request('GET', $urls[0], ['stream' => true, 'headers' => $headers]); + } } diff --git a/classes/EmptyUrlException.php b/classes/exceptions/EmptyUrlException.php similarity index 85% rename from classes/EmptyUrlException.php rename to classes/exceptions/EmptyUrlException.php index 663501c..232559a 100644 --- a/classes/EmptyUrlException.php +++ b/classes/exceptions/EmptyUrlException.php @@ -3,7 +3,7 @@ * EmptyUrlException class. */ -namespace Alltube; +namespace Alltube\Exception; use Exception; diff --git a/classes/PasswordException.php b/classes/exceptions/PasswordException.php similarity index 85% rename from classes/PasswordException.php rename to classes/exceptions/PasswordException.php index 598ca92..256d9c8 100644 --- a/classes/PasswordException.php +++ b/classes/exceptions/PasswordException.php @@ -3,7 +3,7 @@ * PasswordException class. */ -namespace Alltube; +namespace Alltube\Exception; use Exception; diff --git a/classes/streams/ConvertedPlaylistArchiveStream.php b/classes/streams/ConvertedPlaylistArchiveStream.php new file mode 100644 index 0000000..b3b1839 --- /dev/null +++ b/classes/streams/ConvertedPlaylistArchiveStream.php @@ -0,0 +1,33 @@ +curVideoStream = new Stream($video->getAudioStream()); + + $this->init_file_stream_transfer( + $video->getFileNameWithExtension('mp3'), + // The ZIP format does not care about the file size. + 0 + ); + } +} diff --git a/classes/streams/PlaylistArchiveStream.php b/classes/streams/PlaylistArchiveStream.php new file mode 100644 index 0000000..7173c0b --- /dev/null +++ b/classes/streams/PlaylistArchiveStream.php @@ -0,0 +1,304 @@ +buffer = $buffer; + } + foreach ($video->entries as $entry) { + $this->videos[] = new Video($entry->url); + } + } + + /** + * Add data to the archive. + * + * @param string $data Data + * + * @return void + */ + protected function send($data) + { + $pos = $this->tell(); + + // Add data to the end of the buffer. + $this->seek(0, SEEK_END); + $this->write($data); + if ($pos !== false) { + // Rewind so that read() can later read this data. + $this->seek($pos); + } + } + + /** + * Write data to the stream. + * + * @param string $string The string that is to be written + * + * @return int + */ + public function write($string) + { + fwrite($this->buffer, $string); + } + + /** + * Get the size of the stream if known. + * + * @return null + */ + public function getSize() + { + } + + /** + * Returns whether or not the stream is seekable. + * + * @return bool + */ + public function isSeekable() + { + return true; + } + + /** + * Seek to the beginning of the stream. + * + * @return void + */ + public function rewind() + { + rewind($this->buffer); + } + + /** + * Returns whether or not the stream is writable. + * + * @return bool + */ + public function isWritable() + { + return true; + } + + /** + * Returns whether or not the stream is readable. + * + * @return bool + */ + public function isReadable() + { + return true; + } + + /** + * Returns the remaining contents in a string. + * + * @return string + */ + public function getContents() + { + return stream_get_contents($this->buffer); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * @param string $key string $key Specific metadata to retrieve. + * + * @return array|mixed|null + */ + public function getMetadata($key = null) + { + $meta = stream_get_meta_data($this->buffer); + + if (!isset($key)) { + return $meta; + } + + if (isset($meta[$key])) { + return $meta[$key]; + } + } + + /** + * Separates any underlying resources from the stream. + * + * @return resource + */ + public function detach() + { + $stream = $this->buffer; + $this->close(); + + return $stream; + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + * + * @return string + */ + public function __toString() + { + $this->rewind(); + + return $this->getContents(); + } + + /** + * Returns the current position of the file read/write pointer. + * + * @return int|false + */ + public function tell() + { + return ftell($this->buffer); + } + + /** + * Seek to a position in the stream. + * + * @param int $offset Offset + * @param int $whence Specifies how the cursor position will be calculated + * + * @return void + */ + public function seek($offset, $whence = SEEK_SET) + { + fseek($this->buffer, $offset, $whence); + } + + /** + * Returns true if the stream is at the end of the archive. + * + * @return bool + */ + public function eof() + { + return $this->isComplete && feof($this->buffer); + } + + /** + * Start streaming a new video. + * + * @param Video $video Video to stream + * + * @return void + */ + protected function startVideoStream(Video $video) + { + $response = $video->getHttpResponse(); + + $this->curVideoStream = $response->getBody(); + $contentLengthHeaders = $response->getHeader('Content-Length'); + + $this->init_file_stream_transfer( + $video->getFilename(), + $contentLengthHeaders[0] + ); + } + + /** + * Read data from the stream. + * + * @param int $count Number of bytes to read + * + * @return string|false + */ + public function read($count) + { + // If the archive is complete, we only read the remaining buffer. + if (!$this->isComplete) { + if (isset($this->curVideoStream)) { + if ($this->curVideoStream->eof()) { + // Stop streaming the current video. + $this->complete_file_stream(); + + $video = next($this->videos); + if ($video) { + // Start streaming the next video. + $this->startVideoStream($video); + } else { + // No video left. + $this->finish(); + $this->isComplete = true; + } + } else { + // Continue streaming the current video. + $this->stream_file_part($this->curVideoStream->read($count)); + } + } else { + // Start streaming the first video. + $this->startVideoStream(current($this->videos)); + } + } + + return fread($this->buffer, $count); + } + + /** + * Closes the stream and any underlying resources. + * + * @return void + */ + public function close() + { + if (is_resource($this->buffer)) { + fclose($this->buffer); + } + if (isset($this->curVideoStream)) { + $this->curVideoStream->close(); + } + } +} diff --git a/classes/streams/YoutubeChunkStream.php b/classes/streams/YoutubeChunkStream.php new file mode 100644 index 0000000..25c3ea8 --- /dev/null +++ b/classes/streams/YoutubeChunkStream.php @@ -0,0 +1,196 @@ +response = $response; + } + + /** + * Read data from the stream. + * + * @param int $length Read up to $length bytes from the object and return + * + * @return string + */ + public function read($length) + { + $size = $this->response->getHeader('Content-Length')[0]; + if ($size - $this->tell() < $length) { + // Don't try to read further than the end of the stream. + $length = $size - $this->tell(); + } + + return $this->response->getBody()->read($length); + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + */ + public function __toString() + { + return (string) $this->response->getBody(); + } + + /** + * Closes the stream and any underlying resources. + * + * @return mixed + */ + public function close() + { + return $this->response->getBody()->close(); + } + + /** + * Separates any underlying resources from the stream. + * + * @return resource|null + */ + public function detach() + { + return $this->response->getBody()->detach(); + } + + /** + * Get the size of the stream if known. + * + * @return int|null + */ + public function getSize() + { + return $this->response->getBody()->getSize(); + } + + /** + * Returns the current position of the file read/write pointer. + * + * @return int + */ + public function tell() + { + return $this->response->getBody()->tell(); + } + + /** + * Returns true if the stream is at the end of the stream. + * + * @return bool + */ + public function eof() + { + return $this->response->getBody()->eof(); + } + + /** + * Returns whether or not the stream is seekable. + * + * @return bool + */ + public function isSeekable() + { + return $this->response->getBody()->isSeekable(); + } + + /** + * Seek to a position in the stream. + * + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated + * + * @return mixed + */ + public function seek($offset, $whence = SEEK_SET) + { + return $this->response->getBody()->seek($offset, $whence); + } + + /** + * Seek to the beginning of the stream. + * + * @return mixed + */ + public function rewind() + { + return $this->response->getBody()->rewind(); + } + + /** + * Returns whether or not the stream is writable. + * + * @return bool + */ + public function isWritable() + { + return $this->response->getBody()->isWritable(); + } + + /** + * Write data to the stream. + * + * @param string $string The string that is to be written + * + * @return mixed + */ + public function write($string) + { + return $this->response->getBody()->write($string); + } + + /** + * Returns whether or not the stream is readable. + * + * @return bool + */ + public function isReadable() + { + return $this->response->getBody()->isReadable(); + } + + /** + * Returns the remaining contents in a string. + * + * @return string + */ + public function getContents() + { + return $this->response->getBody()->getContents(); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * @param string $key Specific metadata to retrieve. + * + * @return array|mixed|null + */ + public function getMetadata($key = null) + { + return $this->response->getBody()->getMetadata($key); + } +} diff --git a/classes/streams/YoutubeStream.php b/classes/streams/YoutubeStream.php new file mode 100644 index 0000000..9c413aa --- /dev/null +++ b/classes/streams/YoutubeStream.php @@ -0,0 +1,40 @@ +getHttpResponse(); + $contentLenghtHeader = $stream->getHeader('Content-Length'); + $rangeStart = 0; + + while ($rangeStart < $contentLenghtHeader[0]) { + $rangeEnd = $rangeStart + $video->downloader_options->http_chunk_size; + if ($rangeEnd > $contentLenghtHeader[0]) { + $rangeEnd = $contentLenghtHeader[0] - 1; + } + $response = $video->getHttpResponse(['Range' => 'bytes='.$rangeStart.'-'.$rangeEnd]); + $this->addStream(new YoutubeChunkStream($response)); + $rangeStart = $rangeEnd + 1; + } + } +} diff --git a/composer.json b/composer.json index 5ef3ef0..b20218c 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "homepage": "http://alltubedownload.net/", "type": "project", "require": { - "slim/slim": "~3.11.0", + "slim/slim": "~3.12.1", "mathmarques/smarty-view": "~1.1.0", "symfony/yaml": "~3.4.1", "symfony/process": "~3.4.1", @@ -23,9 +23,10 @@ "phpunit/phpunit": "~6.5.2", "doctrine/instantiator": "~1.0.0", "ffmpeg/ffmpeg": "4.0.3", - "rg3/youtube-dl": "2019.01.17", + "rg3/youtube-dl": "2019.04.24", "heroku/heroku-buildpack-php": "*", - "anam/phantomjs-linux-x86-binary": "~2.1.1" + "anam/phantomjs-linux-x86-binary": "~2.1.1", + "phpstan/phpstan": "~0.9.2" }, "extra": { "paas": { @@ -39,10 +40,10 @@ "type": "package", "package": { "name": "rg3/youtube-dl", - "version": "2019.01.17", + "version": "2019.04.24", "dist": { "type": "zip", - "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip" + "url": "https://github.com/rg3/youtube-dl/archive/2019.04.24.zip" } } }, @@ -78,6 +79,8 @@ "autoload": { "psr-4": { "Alltube\\": "classes/", + "Alltube\\Stream\\": "classes/streams/", + "Alltube\\Exception\\": "classes/exceptions/", "Alltube\\Controller\\": "controllers/", "Alltube\\Test\\": "tests/" } diff --git a/composer.lock b/composer.lock index 2ab3bf0..7caa1ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "06c7619047b2e62246e52f538b982231", + "content-hash": "443a32df39c89bd4e58525b41a2de772", "packages": [ { "name": "aura/session", @@ -372,16 +372,16 @@ }, { "name": "mathmarques/smarty-view", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/mathmarques/Smarty-View.git", - "reference": "c8f8501a0be4c290e1165fcb9e5064952ef6969d" + "reference": "2ab996e79efcc600cc324b6469c1cdbcd189c9fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mathmarques/Smarty-View/zipball/c8f8501a0be4c290e1165fcb9e5064952ef6969d", - "reference": "c8f8501a0be4c290e1165fcb9e5064952ef6969d", + "url": "https://api.github.com/repos/mathmarques/Smarty-View/zipball/2ab996e79efcc600cc324b6469c1cdbcd189c9fe", + "reference": "2ab996e79efcc600cc324b6469c1cdbcd189c9fe", "shasum": "" }, "require": { @@ -418,20 +418,20 @@ "template", "view" ], - "time": "2016-08-25T19:04:49+00:00" + "time": "2019-03-31T14:42:41+00:00" }, { "name": "mockery/mockery", - "version": "1.2.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "100633629bf76d57430b86b7098cd6beb996a35a" + "reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/100633629bf76d57430b86b7098cd6beb996a35a", - "reference": "100633629bf76d57430b86b7098cd6beb996a35a", + "url": "https://api.github.com/repos/mockery/mockery/zipball/0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2", + "reference": "0eb0b48c3f07b3b89f5169ce005b7d05b18cf1d2", "shasum": "" }, "require": { @@ -440,7 +440,7 @@ "php": ">=5.6.0" }, "require-dev": { - "phpunit/phpunit": "~5.7.10|~6.5|~7.0" + "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0" }, "type": "library", "extra": { @@ -483,7 +483,7 @@ "test double", "testing" ], - "time": "2018-10-02T21:52:37+00:00" + "time": "2019-02-13T09:37:52+00:00" }, { "name": "nikic/fast-route", @@ -533,27 +533,27 @@ }, { "name": "php-mock/php-mock", - "version": "2.0.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock.git", - "reference": "22d297231118e6fd5b9db087fbe1ef866c2b95d2" + "reference": "e2eea560cb01502148ca895221f0b58806c5a4df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock/zipball/22d297231118e6fd5b9db087fbe1ef866c2b95d2", - "reference": "22d297231118e6fd5b9db087fbe1ef866c2b95d2", + "url": "https://api.github.com/repos/php-mock/php-mock/zipball/e2eea560cb01502148ca895221f0b58806c5a4df", + "reference": "e2eea560cb01502148ca895221f0b58806c5a4df", "shasum": "" }, "require": { - "php": ">=5.6", + "php": "^5.6 || ^7.0", "phpunit/php-text-template": "^1" }, "replace": { "malkusch/php-mock": "*" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0" }, "suggest": { "php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock." @@ -565,7 +565,10 @@ "classes/", "tests/" ] - } + }, + "files": [ + "autoload.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -590,7 +593,7 @@ "test", "test double" ], - "time": "2017-02-17T20:52:52+00:00" + "time": "2019-04-05T22:15:19+00:00" }, { "name": "php-mock/php-mock-integration", @@ -1005,16 +1008,16 @@ }, { "name": "slim/slim", - "version": "3.11.0", + "version": "3.12.1", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a" + "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/d378e70431e78ee92ee32ddde61ecc72edf5dc0a", - "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/eaee12ef8d0750db62b8c548016d82fb33addb6b", + "reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b", "shasum": "" }, "require": { @@ -1072,7 +1075,7 @@ "micro", "router" ], - "time": "2018-09-16T10:54:21+00:00" + "time": "2019-04-16T16:47:29+00:00" }, { "name": "smarty-gettext/smarty-gettext", @@ -1184,16 +1187,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "reference": "82ebae02209c21113908c229e9883c419720738a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", "shasum": "" }, "require": { @@ -1205,7 +1208,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -1227,7 +1230,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -1238,20 +1241,20 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/process", - "version": "v3.4.21", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c" + "reference": "a9c4dfbf653023b668c282e4e02609d131f4057a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", + "url": "https://api.github.com/repos/symfony/process/zipball/a9c4dfbf653023b668c282e4e02609d131f4057a", + "reference": "a9c4dfbf653023b668c282e4e02609d131f4057a", "shasum": "" }, "require": { @@ -1287,20 +1290,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-01-02T21:24:08+00:00" + "time": "2019-04-08T16:15:54+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.21", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea" + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/554a59a1ccbaac238a89b19c8e551a556fd0e2ea", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea", + "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", "shasum": "" }, "require": { @@ -1346,7 +1349,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2019-03-25T07:48:46+00:00" }, { "name": "zonuexe/http-accept-language", @@ -1492,9 +1495,7 @@ "version": "4.0.3", "dist": { "type": "xz", - "url": "https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz", - "reference": null, - "shasum": null + "url": "https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz" }, "bin": [ "ffmpeg" @@ -1503,16 +1504,16 @@ }, { "name": "heroku/heroku-buildpack-php", - "version": "v148", + "version": "v154", "source": { "type": "git", "url": "https://github.com/heroku/heroku-buildpack-php.git", - "reference": "d331bfb9251d8a091d3a0d29e25ffcdb801577e1" + "reference": "2625279c4b3caf5e2937308d07d4357b411d04d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/d331bfb9251d8a091d3a0d29e25ffcdb801577e1", - "reference": "d331bfb9251d8a091d3a0d29e25ffcdb801577e1", + "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/2625279c4b3caf5e2937308d07d4357b411d04d0", + "reference": "2625279c4b3caf5e2937308d07d4357b411d04d0", "shasum": "" }, "bin": [ @@ -1543,7 +1544,58 @@ "nginx", "php" ], - "time": "2018-12-20T21:53:40+00:00" + "time": "2019-04-04T22:05:24+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "1.2", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/75c7effcf3f77501d0e0caa75111aff4daa0dd48", + "reference": "75c7effcf3f77501d0e0caa75111aff4daa0dd48", + "shasum": "" + }, + "require": { + "ocramius/package-versions": "^1.2.0", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "time": "2018-06-13T13:22:40+00:00" }, { "name": "myclabs/deep-copy", @@ -1590,6 +1642,583 @@ ], "time": "2017-10-19T19:58:43+00:00" }, + { + "name": "nette/bootstrap", + "version": "v2.4.6", + "source": { + "type": "git", + "url": "https://github.com/nette/bootstrap.git", + "reference": "268816e3f1bb7426c3a4ceec2bd38a036b532543" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/bootstrap/zipball/268816e3f1bb7426c3a4ceec2bd38a036b532543", + "reference": "268816e3f1bb7426c3a4ceec2bd38a036b532543", + "shasum": "" + }, + "require": { + "nette/di": "~2.4.7", + "nette/utils": "~2.4", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "latte/latte": "~2.2", + "nette/application": "~2.3", + "nette/caching": "~2.3", + "nette/database": "~2.3", + "nette/forms": "~2.3", + "nette/http": "~2.4.0", + "nette/mail": "~2.3", + "nette/robot-loader": "^2.4.2 || ^3.0", + "nette/safe-stream": "~2.2", + "nette/security": "~2.3", + "nette/tester": "~2.0", + "tracy/tracy": "^2.4.1" + }, + "suggest": { + "nette/robot-loader": "to use Configurator::createRobotLoader()", + "tracy/tracy": "to use Configurator::enableTracy()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette Bootstrap: the simple way to configure and bootstrap your Nette application.", + "homepage": "https://nette.org", + "keywords": [ + "bootstrapping", + "configurator", + "nette" + ], + "time": "2018-05-17T12:52:20+00:00" + }, + { + "name": "nette/di", + "version": "v2.4.15", + "source": { + "type": "git", + "url": "https://github.com/nette/di.git", + "reference": "d0561b8f77e8ef2ed6d83328860e16c81a5a8649" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/di/zipball/d0561b8f77e8ef2ed6d83328860e16c81a5a8649", + "reference": "d0561b8f77e8ef2ed6d83328860e16c81a5a8649", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/neon": "^2.3.3 || ~3.0.0", + "nette/php-generator": "^2.6.1 || ^3.0.0", + "nette/utils": "^2.5.0 || ~3.0.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/bootstrap": "<2.4", + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP 7.1 features.", + "homepage": "https://nette.org", + "keywords": [ + "compiled", + "di", + "dic", + "factory", + "ioc", + "nette", + "static" + ], + "time": "2019-01-30T13:26:05+00:00" + }, + { + "name": "nette/finder", + "version": "v2.4.2", + "source": { + "type": "git", + "url": "https://github.com/nette/finder.git", + "reference": "ee951a656cb8ac622e5dd33474a01fd2470505a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/finder/zipball/ee951a656cb8ac622e5dd33474a01fd2470505a0", + "reference": "ee951a656cb8ac622e5dd33474a01fd2470505a0", + "shasum": "" + }, + "require": { + "nette/utils": "~2.4", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette Finder: find files and directories with an intuitive API.", + "homepage": "https://nette.org", + "keywords": [ + "filesystem", + "glob", + "iterator", + "nette" + ], + "time": "2018-06-28T11:49:23+00:00" + }, + { + "name": "nette/neon", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/nette/neon.git", + "reference": "cbff32059cbdd8720deccf9e9eace6ee516f02eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/neon/zipball/cbff32059cbdd8720deccf9e9eace6ee516f02eb", + "reference": "cbff32059cbdd8720deccf9e9eace6ee516f02eb", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "ext-json": "*", + "php": ">=7.0" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette NEON: encodes and decodes NEON file format.", + "homepage": "http://ne-on.org", + "keywords": [ + "export", + "import", + "neon", + "nette", + "yaml" + ], + "time": "2019-02-05T21:30:40+00:00" + }, + { + "name": "nette/php-generator", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/nette/php-generator.git", + "reference": "ea90209c2e8a7cd087b2742ca553c047a8df5eff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/php-generator/zipball/ea90209c2e8a7cd087b2742ca553c047a8df5eff", + "reference": "ea90209c2e8a7cd087b2742ca553c047a8df5eff", + "shasum": "" + }, + "require": { + "nette/utils": "^2.4.2 || ~3.0.0", + "php": ">=7.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.2 features.", + "homepage": "https://nette.org", + "keywords": [ + "code", + "nette", + "php", + "scaffolding" + ], + "time": "2018-08-09T14:32:27+00:00" + }, + { + "name": "nette/robot-loader", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/nette/robot-loader.git", + "reference": "3e8d75d6d976e191bdf46752ca40a286671219d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/robot-loader/zipball/3e8d75d6d976e191bdf46752ca40a286671219d2", + "reference": "3e8d75d6d976e191bdf46752ca40a286671219d2", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/finder": "^2.3 || ^3.0", + "nette/utils": "^2.4 || ^3.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.", + "homepage": "https://nette.org", + "keywords": [ + "autoload", + "class", + "interface", + "nette", + "trait" + ], + "time": "2019-03-01T20:23:02+00:00" + }, + { + "name": "nette/utils", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/17b9f76f2abd0c943adfb556e56f2165460b15ce", + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize() and toAscii()", + "ext-intl": "for script transliteration in Strings::webalize() and toAscii()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/loader.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "? Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "time": "2018-09-18T10:22:16+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2018-02-28T20:30:58+00:00" + }, + { + "name": "ocramius/package-versions", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "ad8a245decad4897cc6b432743913dad0d69753c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/ad8a245decad4897cc6b432743913dad0d69753c", + "reference": "ad8a245decad4897cc6b432743913dad0d69753c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "~7.0" + }, + "require-dev": { + "composer/composer": "^1.3", + "ext-zip": "*", + "humbug/humbug": "dev-master", + "phpunit/phpunit": "^6.4" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2017-11-24T11:07:03+00:00" + }, { "name": "phar-io/manifest", "version": "1.0.1", @@ -1907,6 +2536,114 @@ ], "time": "2018-08-05T17:53:17+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "0.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "02f909f134fe06f0cd4790d8627ee24efbe84d6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/02f909f134fe06f0cd4790d8627ee24efbe84d6a", + "reference": "02f909f134fe06f0cd4790d8627ee24efbe84d6a", + "shasum": "" + }, + "require": { + "php": "~7.0" + }, + "require-dev": { + "consistence/coding-standard": "^2.0.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/phpstan": "^0.9", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^3.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.1-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "time": "2018-01-13T18:19:41+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "0.9.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "e59541bcc7cac9b35ca54db6365bf377baf4a488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e59541bcc7cac9b35ca54db6365bf377baf4a488", + "reference": "e59541bcc7cac9b35ca54db6365bf377baf4a488", + "shasum": "" + }, + "require": { + "jean85/pretty-package-versions": "^1.0.3", + "nette/bootstrap": "^2.4 || ^3.0", + "nette/di": "^2.4.7 || ^3.0", + "nette/robot-loader": "^3.0.1", + "nette/utils": "^2.4.5 || ^3.0", + "nikic/php-parser": "^3.1", + "php": "~7.0", + "phpstan/phpdoc-parser": "^0.2", + "symfony/console": "~3.2 || ~4.0", + "symfony/finder": "~3.2 || ~4.0" + }, + "require-dev": { + "consistence/coding-standard": "2.2.1", + "ext-gd": "*", + "ext-intl": "*", + "ext-mysqli": "*", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/phpstan-php-parser": "^0.9", + "phpstan/phpstan-phpunit": "^0.9.3", + "phpstan/phpstan-strict-rules": "^0.9", + "phpunit/phpunit": "^6.5.4", + "slevomat/coding-standard": "4.0.0" + }, + "bin": [ + "bin/phpstan" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": [ + "src/", + "build/PHPStan" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "time": "2018-01-28T13:22:19+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "5.3.2", @@ -2117,16 +2854,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.13", + "version": "6.5.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693" + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", "shasum": "" }, "require": { @@ -2197,7 +2934,7 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" + "time": "2019-02-01T05:22:47+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -2256,16 +2993,62 @@ "mock", "xunit" ], + "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, { - "name": "rg3/youtube-dl", - "version": "2019.01.17", + "name": "psr/log", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + }, "dist": { "type": "zip", - "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip", - "reference": null, - "shasum": null + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2018-11-20T15:27:04+00:00" + }, + { + "name": "rg3/youtube-dl", + "version": "2019.04.24", + "dist": { + "type": "zip", + "url": "https://github.com/rg3/youtube-dl/archive/2019.04.24.zip" }, "type": "library" }, @@ -2830,16 +3613,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.4.0", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "379deb987e26c7cd103a7b387aea178baec96e48" + "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/379deb987e26c7cd103a7b387aea178baec96e48", - "reference": "379deb987e26c7cd103a7b387aea178baec96e48", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", + "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", "shasum": "" }, "require": { @@ -2872,25 +3655,202 @@ } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", "standards" ], - "time": "2018-12-19T23:57:18+00:00" + "time": "2019-04-10T23:49:02+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "name": "symfony/console", + "version": "v3.4.26", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "url": "https://github.com/symfony/console.git", + "reference": "15a9104356436cb26e08adab97706654799d31d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/console/zipball/15a9104356436cb26e08adab97706654799d31d8", + "reference": "15a9104356436cb26e08adab97706654799d31d8", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2019-04-08T09:29:13+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.26", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/681afbb26488903c5ac15e63734f1d8ac430c9b9", + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2019-04-11T09:48:14+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.4.26", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/61af5ce0b34b942d414fe8f1b11950d0e9a90e98", + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2019-04-02T19:54:57+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", "shasum": "" }, "require": { @@ -2902,7 +3862,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -2936,20 +3896,20 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.4.21", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2" + "reference": "f0883812642a6d6583a9e2ae6aec4ba134436f40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a5f39641bb62e8b74e343467b145331273f615a2", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f0883812642a6d6583a9e2ae6aec4ba134436f40", + "reference": "f0883812642a6d6583a9e2ae6aec4ba134436f40", "shasum": "" }, "require": { @@ -3005,20 +3965,20 @@ "debug", "dump" ], - "time": "2019-01-01T13:45:19+00:00" + "time": "2019-04-16T13:58:17+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", "shasum": "" }, "require": { @@ -3045,7 +4005,7 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" + "time": "2019-04-04T09:56:43+00:00" }, { "name": "webmozart/assert", diff --git a/controllers/BaseController.php b/controllers/BaseController.php new file mode 100644 index 0000000..f96842e --- /dev/null +++ b/controllers/BaseController.php @@ -0,0 +1,109 @@ +config = Config::getInstance(); + $this->container = $container; + $session = SessionManager::getSession(); + $this->sessionSegment = $session->getSegment(self::class); + + if ($this->config->stream) { + $this->defaultFormat = 'best'; + } + } + + /** + * Get video format from request parameters or default format if none is specified. + * + * @param Request $request PSR-7 request + * + * @return string format + */ + protected function getFormat(Request $request) + { + $format = $request->getQueryParam('format'); + if (!isset($format)) { + $format = $this->defaultFormat; + } + + return $format; + } + + /** + * Get the password entered for the current video. + * + * @param Request $request PSR-7 request + * + * @return string Password + */ + protected function getPassword(Request $request) + { + $url = $request->getQueryParam('url'); + + $password = $request->getParam('password'); + if (isset($password)) { + $this->sessionSegment->setFlash($url, $password); + } else { + $password = $this->sessionSegment->getFlash($url); + } + + return $password; + } +} diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php new file mode 100644 index 0000000..ddd83b3 --- /dev/null +++ b/controllers/DownloadController.php @@ -0,0 +1,281 @@ +getQueryParam('url'); + + if (isset($url)) { + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); + + try { + if ($this->config->convert && $request->getQueryParam('audio')) { + // Audio convert. + return $this->getAudioResponse($request, $response); + } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { + // Advance convert. + return $this->getConvertedResponse($request, $response); + } + + // Regular download. + return $this->getDownloadResponse($request, $response); + } catch (PasswordException $e) { + return $response->withRedirect( + $this->container->get('router')->pathFor('info').'?'.http_build_query($request->getQueryParams()) + ); + } catch (Exception $e) { + $response->getBody()->write($e->getMessage()); + + return $response->withHeader('Content-Type', 'text/plain')->withStatus(500); + } + } else { + return $response->withRedirect($this->container->get('router')->pathFor('index')); + } + } + + /** + * Return a converted MP3 file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getConvertedAudioResponse(Request $request, Response $response) + { + $from = $request->getQueryParam('from'); + $to = $request->getQueryParam('to'); + + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFileNameWithExtension('mp3').'"' + ); + $response = $response->withHeader('Content-Type', 'audio/mpeg'); + + if ($request->isGet() || $request->isPost()) { + try { + $process = $this->video->getAudioStream($from, $to); + } catch (Exception $e) { + // Fallback to default format. + $this->video = $this->video->withFormat($this->defaultFormat); + $process = $this->video->getAudioStream($from, $to); + } + $response = $response->withBody(new Stream($process)); + } + + return $response; + } + + /** + * Return the MP3 file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getAudioResponse(Request $request, Response $response) + { + try { + // First, we try to get a MP3 file directly. + if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { + throw new Exception('Force convert when we need to seek.'); + } + + if ($this->config->stream) { + $this->video = $this->video->withFormat('mp3'); + + return $this->getStream($request, $response); + } else { + $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]'); + + $urls = $this->video->getUrl(); + + return $response->withRedirect($urls[0]); + } + } catch (PasswordException $e) { + $frontController = new FrontController($this->container); + + return $frontController->password($request, $response); + } catch (Exception $e) { + // If MP3 is not available, we convert it. + $this->video = $this->video->withFormat($this->defaultFormat); + + return $this->getConvertedAudioResponse($request, $response); + } + } + + /** + * Get a video/audio stream piped through the server. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getStream(Request $request, Response $response) + { + if (isset($this->video->entries)) { + if ($this->config->convert && $request->getQueryParam('audio')) { + $stream = new ConvertedPlaylistArchiveStream($this->video); + } else { + $stream = new PlaylistArchiveStream($this->video); + } + $response = $response->withHeader('Content-Type', 'application/zip'); + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'.$this->video->title.'.zip"' + ); + + return $response->withBody($stream); + } elseif ($this->video->protocol == 'rtmp') { + $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); + $body = new Stream($this->video->getRtmpStream()); + } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') { + $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); + $body = new Stream($this->video->getM3uStream()); + } else { + $stream = $this->video->getHttpResponse(['Range' => $request->getHeader('Range')]); + + $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); + $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); + $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges')); + $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range')); + if ($stream->getStatusCode() == 206) { + $response = $response->withStatus(206); + } + + if (isset($this->video->downloader_options->http_chunk_size)) { + // Workaround for Youtube throttling the download speed. + $body = new YoutubeStream($this->video); + } else { + $body = $stream->getBody(); + } + } + if ($request->isGet()) { + $response = $response->withBody($body); + } + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFilename().'"' + ); + + return $response; + } + + /** + * Get a remuxed stream piped through the server. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getRemuxStream(Request $request, Response $response) + { + if (!$this->config->remux) { + throw new Exception(_('You need to enable remux mode to merge two formats.')); + } + $stream = $this->video->getRemuxStream(); + $response = $response->withHeader('Content-Type', 'video/x-matroska'); + if ($request->isGet()) { + $response = $response->withBody(new Stream($stream)); + } + + return $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv') + ); + } + + /** + * Get approriate HTTP response to download query. + * Depends on whether we want to stream, remux or simply redirect. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getDownloadResponse(Request $request, Response $response) + { + try { + $videoUrls = $this->video->getUrl(); + } catch (EmptyUrlException $e) { + /* + If this happens it is probably a playlist + so it will either be handled by getStream() or throw an exception anyway. + */ + $videoUrls = []; + } + if (count($videoUrls) > 1) { + return $this->getRemuxStream($request, $response); + } elseif ($this->config->stream && (isset($this->video->entries) || $request->getQueryParam('stream'))) { + return $this->getStream($request, $response); + } else { + if (empty($videoUrls[0])) { + throw new Exception(_("Can't find URL of video.")); + } + + return $response->withRedirect($videoUrls[0]); + } + } + + /** + * Return a converted video file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getConvertedResponse(Request $request, Response $response) + { + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"' + ); + $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat')); + + if ($request->isGet() || $request->isPost()) { + $process = $this->video->getConvertedStream( + $request->getQueryParam('customBitrate'), + $request->getQueryParam('customFormat') + ); + $response = $response->withBody(new Stream($process)); + } + + return $response; + } +} diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 7c27d75..c89cad4 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -6,55 +6,22 @@ namespace Alltube\Controller; use Alltube\Config; -use Alltube\EmptyUrlException; +use Alltube\Exception\PasswordException; use Alltube\Locale; use Alltube\LocaleManager; -use Alltube\PasswordException; -use Alltube\VideoDownload; -use Aura\Session\Segment; -use Aura\Session\SessionFactory; +use Alltube\Video; use Exception; -use GuzzleHttp\Client; use Psr\Container\ContainerInterface; use Slim\Container; use Slim\Http\Request; use Slim\Http\Response; -use Slim\Http\Stream; use Slim\Views\Smarty; /** * Main controller. */ -class FrontController +class FrontController extends BaseController { - /** - * Config instance. - * - * @var Config - */ - private $config; - - /** - * VideoDownload instance. - * - * @var VideoDownload - */ - private $download; - - /** - * Slim dependency container. - * - * @var ContainerInterface - */ - private $container; - - /** - * Session segment used to store session variables. - * - * @var Segment - */ - private $sessionSegment; - /** * Smarty view. * @@ -62,13 +29,6 @@ class FrontController */ private $view; - /** - * Default youtube-dl format. - * - * @var string - */ - private $defaultFormat = 'best[protocol^=http]'; - /** * LocaleManager instance. * @@ -77,31 +37,16 @@ class FrontController private $localeManager; /** - * FrontController constructor. + * BaseController constructor. * - * @param Container $container Slim dependency container - * @param Config $config Config instance - * @param array $cookies Cookie array + * @param ContainerInterface $container Slim dependency container */ - public function __construct(ContainerInterface $container, Config $config = null, array $cookies = []) + public function __construct(ContainerInterface $container) { - if (isset($config)) { - $this->config = $config; - } else { - $this->config = Config::getInstance(); - } - $this->download = new VideoDownload($this->config); - $this->container = $container; - $this->view = $this->container->get('view'); + parent::__construct($container); + $this->localeManager = $this->container->get('locale'); - $session_factory = new SessionFactory(); - $session = $session_factory->newInstance($cookies); - $this->sessionSegment = $session->getSegment(self::class); - if ($this->config->remux) { - $this->defaultFormat = 'bestvideo+bestaudio,best'; - } elseif ($this->config->stream) { - $this->defaultFormat = 'best'; - } + $this->view = $this->container->get('view'); } /** @@ -114,7 +59,7 @@ class FrontController */ public function index(Request $request, Response $response) { - $uri = $request->getUri()->withUserInfo(null); + $uri = $request->getUri()->withUserInfo(''); $this->view->render( $response, 'index.tpl', @@ -163,7 +108,7 @@ class FrontController 'extractors.tpl', [ 'config' => $this->config, - 'extractors' => $this->download->listExtractors(), + 'extractors' => Video::getExtractors(), 'class' => 'extractors', 'title' => _('Supported websites'), 'description' => _('List of all supported websites from which Alltube Download '. @@ -202,132 +147,45 @@ class FrontController return $response; } - /** - * Return a converted MP3 file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * @param array $params GET query parameters - * @param string $password Video password - * - * @return Response HTTP response - */ - private function getConvertedAudioResponse(Request $request, Response $response, array $params, $password = null) - { - if (!isset($params['from'])) { - $params['from'] = ''; - } - if (!isset($params['to'])) { - $params['to'] = ''; - } - - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"' - ); - $response = $response->withHeader('Content-Type', 'audio/mpeg'); - - if ($request->isGet() || $request->isPost()) { - try { - $process = $this->download->getAudioStream( - $params['url'], - 'bestaudio/best', - $password, - $params['from'], - $params['to'] - ); - } catch (Exception $e) { - $process = $this->download->getAudioStream( - $params['url'], - $this->defaultFormat, - $password, - $params['from'], - $params['to'] - ); - } - $response = $response->withBody(new Stream($process)); - } - - return $response; - } - - /** - * Return the MP3 file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * @param array $params GET query parameters - * @param string $password Video password - * - * @return Response HTTP response - */ - private function getAudioResponse(Request $request, Response $response, array $params, $password = null) - { - try { - if (isset($params['from']) || isset($params['to'])) { - throw new Exception('Force convert when we need to seek.'); - } - - if ($this->config->stream) { - return $this->getStream($params['url'], 'mp3', $response, $request, $password); - } else { - $urls = $this->download->getURL($params['url'], 'mp3[protocol^=http]', $password); - - return $response->withRedirect($urls[0]); - } - } catch (PasswordException $e) { - return $this->password($request, $response); - } catch (Exception $e) { - return $this->getConvertedAudioResponse($request, $response, $params, $password); - } - } - /** * Return the video description page. * * @param Request $request PSR-7 request * @param Response $response PSR-7 response - * @param array $params GET query parameters - * @param string $password Video password * * @return Response HTTP response */ - private function getVideoResponse(Request $request, Response $response, array $params, $password = null) + private function getInfoResponse(Request $request, Response $response) { try { - $video = $this->download->getJSON($params['url'], $this->defaultFormat, $password); + $this->video->getJson(); } catch (PasswordException $e) { return $this->password($request, $response); } - if ($this->config->stream) { - $protocol = ''; - } else { - $protocol = '[protocol^=http]'; - } - if (isset($video->entries)) { + + if (isset($this->video->entries)) { $template = 'playlist.tpl'; } else { - $template = 'video.tpl'; + $template = 'info.tpl'; } $title = _('Video download'); - $description = _('Download video from ').$video->extractor_key; - if (isset($video->title)) { - $title = $video->title; - $description = _('Download').' "'.$video->title.'" '._('from').' '.$video->extractor_key; + $description = _('Download video from ').$this->video->extractor_key; + if (isset($this->video->title)) { + $title = $this->video->title; + $description = _('Download').' "'.$this->video->title.'" '._('from').' '.$this->video->extractor_key; } $this->view->render( $response, $template, [ - 'video' => $video, - 'class' => 'video', - 'title' => $title, - 'description' => $description, - 'protocol' => $protocol, - 'config' => $this->config, - 'canonical' => $this->getCanonicalUrl($request), - 'locale' => $this->localeManager->getLocale(), + 'video' => $this->video, + 'class' => 'info', + 'title' => $title, + 'description' => $description, + 'config' => $this->config, + 'canonical' => $this->getCanonicalUrl($request), + 'locale' => $this->localeManager->getLocale(), + 'defaultFormat' => $this->defaultFormat, ] ); @@ -342,23 +200,21 @@ class FrontController * * @return Response HTTP response */ - public function video(Request $request, Response $response) + public function info(Request $request, Response $response) { - $params = $request->getQueryParams(); + $url = $request->getQueryParam('url') ?: $request->getQueryParam('v'); - if (!isset($params['url']) && isset($params['v'])) { - $params['url'] = $params['v']; - } + if (isset($url) && !empty($url)) { + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); - if (isset($params['url']) && !empty($params['url'])) { - $password = $request->getParam('password'); - if (isset($password)) { - $this->sessionSegment->setFlash($params['url'], $password); - } - if (isset($params['audio'])) { - return $this->getAudioResponse($request, $response, $params, $password); + if ($this->config->convert && $request->getQueryParam('audio')) { + // We skip the info page and get directly to the download. + return $response->withRedirect( + $this->container->get('router')->pathFor('download'). + '?'.http_build_query($request->getQueryParams()) + ); } else { - return $this->getVideoResponse($request, $response, $params, $password); + return $this->getInfoResponse($request, $response); } } else { return $response->withRedirect($this->container->get('router')->pathFor('index')); @@ -392,265 +248,6 @@ class FrontController return $response->withStatus(500); } - /** - * Get a video/audio stream piped through the server. - * - * @param string $url URL of the video - * @param string $format Requested format - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * @param string $password Video password - * - * @return Response HTTP response - */ - private function getStream($url, $format, Response $response, Request $request, $password = null) - { - $video = $this->download->getJSON($url, $format, $password); - if (isset($video->entries)) { - $stream = $this->download->getPlaylistArchiveStream($video, $format); - $response = $response->withHeader('Content-Type', 'application/x-tar'); - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'.$video->title.'.tar"' - ); - - return $response->withBody(new Stream($stream)); - } elseif ($video->protocol == 'rtmp') { - $stream = $this->download->getRtmpStream($video); - $response = $response->withHeader('Content-Type', 'video/'.$video->ext); - $body = new Stream($stream); - } elseif ($video->protocol == 'm3u8' || $video->protocol == 'm3u8_native') { - $stream = $this->download->getM3uStream($video); - $response = $response->withHeader('Content-Type', 'video/'.$video->ext); - $body = new Stream($stream); - } else { - $client = new Client(); - $stream = $client->request( - 'GET', - $video->url, - [ - 'stream' => true, - 'headers' => ['Range' => $request->getHeader('Range')], - ] - ); - $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); - $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); - $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges')); - $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range')); - if ($stream->getStatusCode() == 206) { - $response = $response->withStatus(206); - } - $body = $stream->getBody(); - } - if ($request->isGet()) { - $response = $response->withBody($body); - } - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->download->getFilename($url, $format, $password).'"' - ); - - return $response; - } - - /** - * Get a remuxed stream piped through the server. - * - * @param string[] $urls URLs of the video and audio files - * @param string $format Requested format - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * - * @return Response HTTP response - */ - private function getRemuxStream(array $urls, $format, Response $response, Request $request) - { - if (!$this->config->remux) { - throw new Exception(_('You need to enable remux mode to merge two formats.')); - } - $stream = $this->download->getRemuxStream($urls); - $response = $response->withHeader('Content-Type', 'video/x-matroska'); - if ($request->isGet()) { - $response = $response->withBody(new Stream($stream)); - } - $webpageUrl = $request->getQueryParam('url'); - - return $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'.$this->download->getFileNameWithExtension( - 'mkv', - $webpageUrl, - $format, - $this->sessionSegment->getFlash($webpageUrl) - ) - ); - } - - /** - * Get video format from request parameters or default format if none is specified. - * - * @param Request $request PSR-7 request - * - * @return string format - */ - private function getFormat(Request $request) - { - $format = $request->getQueryParam('format'); - if (!isset($format)) { - $format = $this->defaultFormat; - } - - return $format; - } - - /** - * Get approriate HTTP response to redirect query - * Depends on whether we want to stream, remux or simply redirect. - * - * @param string $url URL of the video - * @param string $format Requested format - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * - * @return Response HTTP response - */ - private function getRedirectResponse($url, $format, Response $response, Request $request) - { - try { - $videoUrls = $this->download->getURL( - $url, - $format, - $this->sessionSegment->getFlash($url) - ); - } catch (EmptyUrlException $e) { - /* - If this happens it is probably a playlist - so it will either be handled by getStream() or throw an exception anyway. - */ - $videoUrls = []; - } - if (count($videoUrls) > 1) { - return $this->getRemuxStream($videoUrls, $format, $response, $request); - } elseif ($this->config->stream) { - return $this->getStream( - $url, - $format, - $response, - $request, - $this->sessionSegment->getFlash($url) - ); - } else { - if (empty($videoUrls[0])) { - throw new Exception(_("Can't find URL of video.")); - } - - return $response->withRedirect($videoUrls[0]); - } - } - - /** - * Return a converted video file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * @param array $params GET query parameters - * @param string $format Requested source format - * - * @return Response HTTP response - */ - private function getConvertedResponse(Request $request, Response $response, array $params, $format) - { - $password = $request->getParam('password'); - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->download->getFileNameWithExtension( - $params['customFormat'], - $params['url'], - $format, - $password - ).'"' - ); - $response = $response->withHeader('Content-Type', 'video/'.$params['customFormat']); - - if ($request->isGet() || $request->isPost()) { - $process = $this->download->getConvertedStream( - $params['url'], - $format, - $params['customBitrate'], - $params['customFormat'], - $password - ); - $response = $response->withBody(new Stream($process)); - } - - return $response; - } - - /** - * Redirect to video file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - public function redirect(Request $request, Response $response) - { - $params = $request->getQueryParams(); - $format = $this->getFormat($request); - if (isset($params['url'])) { - try { - if ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { - return $this->getConvertedResponse($request, $response, $params, $format); - } - - return $this->getRedirectResponse($params['url'], $format, $response, $request); - } catch (PasswordException $e) { - return $response->withRedirect( - $this->container->get('router')->pathFor('video').'?url='.urlencode($params['url']) - ); - } catch (Exception $e) { - $response->getBody()->write($e->getMessage()); - - return $response->withHeader('Content-Type', 'text/plain')->withStatus(500); - } - } else { - return $response->withRedirect($this->container->get('router')->pathFor('index')); - } - } - - /** - * Return the JSON object generated by youtube-dl. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - public function json(Request $request, Response $response) - { - $params = $request->getQueryParams(); - $format = $this->getFormat($request); - if (isset($params['url'])) { - try { - return $response->withJson( - $this->download->getJSON( - $params['url'], - $format - ) - ); - } catch (Exception $e) { - return $response->withJson(['error' => $e->getMessage()]) - ->withStatus(500); - } - } else { - return $response->withJson(['error' => 'You need to provide the url parameter']) - ->withStatus(400); - } - } - /** * Generate the canonical URL of the current page. * diff --git a/controllers/JsonController.php b/controllers/JsonController.php new file mode 100644 index 0000000..cc36e46 --- /dev/null +++ b/controllers/JsonController.php @@ -0,0 +1,44 @@ +getQueryParam('url'); + + if (isset($url)) { + try { + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); + + return $response->withJson($this->video->getJson()); + } catch (Exception $e) { + return $response->withJson(['error' => $e->getMessage()]) + ->withStatus(500); + } + } else { + return $response->withJson(['error' => 'You need to provide the url parameter']) + ->withStatus(400); + } + } +} diff --git a/css/style.css b/css/style.css index 951ef4d..482ff25 100644 --- a/css/style.css +++ b/css/style.css @@ -1,733 +1,724 @@ - body { - background-color: #EBEBEB; - background-image:url('../img/fond.jpg'); - font-family: 'Open Sans', sans-serif; - font-weight:400; - text-align:center; + background-color: #ebebeb; + background-image: url("../img/fond.jpg"); + font-family: "Open Sans", sans-serif; + font-weight: 400; + text-align: center; } - - /* Header */ header { - padding:0; - position:absolute; - text-align:right; - top:0; - width:100%; + padding: 0; + position: absolute; + text-align: right; + top: 0; + width: 100%; } .social { - padding-right:21px; + padding-right: 21px; } - header .social a { - background-position:0 0; - background-repeat:no-repeat; - float:right; - height:38px; - margin-left:13px; - margin-right:0; - margin-top:13px; - overflow:hidden; - position:relative; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; - width:38px; + background-position: 0 0; + background-repeat: no-repeat; + float: right; + height: 38px; + margin-left: 13px; + margin-right: 0; + margin-top: 13px; + overflow: hidden; + position: relative; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; + width: 38px; } header a:focus, header a:hover { - background-position:0 100%; - outline:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + background-position: 0 100%; + outline: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } .share { - background-image:url('../img/share.png'); + background-image: url("../img/share.png"); } .sharemask { - background-image:url('../img/sharemask.png'); - background-position:top left; - background-repeat:no-repeat; - height:38px; - left:0; - position:absolute; - top:0; - width:38px; - z-index:10; - + background-image: url("../img/sharemask.png"); + background-position: top left; + background-repeat: no-repeat; + height: 38px; + left: 0; + position: absolute; + top: 0; + width: 38px; + z-index: 10; } .facebook { - background-image:url('../img/facebook.png'); + background-image: url("../img/facebook.png"); } .facebookmask { - background-image:url('../img/facebookmask.png'); - background-position:top left; - background-repeat:no-repeat; - height:38px; - left:0; - position:absolute; - top:0; - width:38px; - z-index:10; + background-image: url("../img/facebookmask.png"); + background-position: top left; + background-repeat: no-repeat; + height: 38px; + left: 0; + position: absolute; + top: 0; + width: 38px; + z-index: 10; } .twitter { - background-image:url('../img/twitter.png'); + background-image: url("../img/twitter.png"); } .twittermask { - background-image:url('../img/twittermask.png'); - background-position:top left; - background-repeat:no-repeat; - height:38px; - left:0; - position:absolute; - top:0; - width:38px; - z-index:10; + background-image: url("../img/twittermask.png"); + background-position: top left; + background-repeat: no-repeat; + height: 38px; + left: 0; + position: absolute; + top: 0; + width: 38px; + z-index: 10; } - - /* Footer */ - footer { - background-image:url('../img/fondfooter.png'); - background-position:top left; - background-repeat:repeat-x; - bottom:0; - color:#adadad; - padding-top:20px; - position:fixed; - text-align:center; - width:100%; - z-index:11; + background-image: url("../img/fondfooter.png"); + background-position: top left; + background-repeat: repeat-x; + bottom: 0; + color: #adadad; + padding-top: 20px; + position: fixed; + text-align: center; + width: 100%; + z-index: 11; } .footer_wrapper { - height:28px; + height: 28px; } -footer a{ - color:#adadad; - text-decoration:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; +footer a { + color: #adadad; + text-decoration: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } footer a:focus, footer a:hover { - color:#f2084a; - outline:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + color: #f2084a; + outline: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } - - - - - /* Home content */ .logo { - padding-bottom:55px; + padding-bottom: 55px; } .labelurl { - color:#3f3f3f; - font-size:19px; - position:relative; + color: #3f3f3f; + font-size: 19px; + position: relative; } .champs { - margin-bottom:70px; - margin-top:8px; - position:relative; + margin-bottom: 70px; + margin-top: 8px; + position: relative; } .downloadBtn { - background-color:#3A3A3A; - border: 3px solid #a5a5a5; - border-radius:10px; - color:#dedede; - cursor:pointer; - display:inline-block; - font-weight:800; - padding: 12px 14px; - position:relative; - text-decoration:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + background-color: #3a3a3a; + border: 3px solid #a5a5a5; + border-radius: 10px; + color: #dedede; + cursor: pointer; + display: inline-block; + font-weight: 800; + padding: 12px 14px; + position: relative; + text-decoration: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } .downloadBtn:focus, .downloadBtn:hover { - background-color:#f2084a; - outline:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + background-color: #f2084a; + outline: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } .downloadBtn::-moz-focus-inner { - border:none; + border: none; } -.URLinput{ - background-color:#fff; - border: 3px solid #a5a5a5; - border-radius:10px; - color:#3F3F3F; - font-weight:800; - margin-right:8px; - min-width:426px; - padding: 12px 12px 12px 12px; - position:relative; +.URLinput { + background-color: #fff; + border: 3px solid #a5a5a5; + border-radius: 10px; + color: #3f3f3f; + font-weight: 800; + margin-right: 8px; + min-width: 426px; + padding: 12px 12px 12px 12px; + position: relative; } - .URLinput:focus { - border-color:#3A3A3A; - outline: none; + border-color: #3a3a3a; + outline: none; } -.URLinput:-webkit-input-placeholder{ - color:#c1cfcf; +.URLinput:-webkit-input-placeholder { + color: #c1cfcf; } -.URLinput:-moz-placeholder { - color:#c1cfcf; +.URLinput:-moz-placeholder { + color: #c1cfcf; } .combatiblelink { - background-image:url('../img/compatiblerouage.png'); - background-position:0 100%; - background-repeat:no-repeat; - color:#a5a5a5; - padding-bottom:10px; - padding-left:41px; - padding-top:10px; - position:relative; - text-decoration:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; - z-index:10; + background-image: url("../img/compatiblerouage.png"); + background-position: 0 100%; + background-repeat: no-repeat; + color: #a5a5a5; + padding-bottom: 10px; + padding-left: 41px; + padding-top: 10px; + position: relative; + text-decoration: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; + z-index: 10; } .combatiblelink:focus, .combatiblelink:hover { - background-position:0 0; - color:#f2084a; - outline:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + background-position: 0 0; + color: #f2084a; + outline: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } .bookmarklet { - border: 2px dotted; - color:gray; - padding:10px 30px; - position:relative; - text-decoration:none; - z-index:10; + border: 2px dotted; + color: gray; + padding: 10px 30px; + position: relative; + text-decoration: none; + z-index: 10; } .mp3 { - background-color:#cecece; - border-radius:6px; - color:#3f3f3f; - height:26px; - margin-top:12px; - position:relative; - text-align:left; - width:622px; + background-color: #cecece; + border-radius: 6px; + color: #3f3f3f; + height: 26px; + margin-top: 12px; + position: relative; + text-align: left; + width: 622px; } .mp3-inner { - padding:3px; + padding: 3px; } .audio:not(:checked), .audio:checked { - left: -9999px; - position: absolute; + left: -9999px; + position: absolute; } .audio:not(:checked) + label, .audio:checked + label { - cursor: pointer; - line-height:20px; - padding-left: 82px; - position: relative; + cursor: pointer; + line-height: 20px; + padding-left: 82px; + position: relative; } .audio:not(:checked) + label:before, .audio:checked + label:before, .audio:not(:checked) + label:after, .audio:checked + label:after { - content: ''; - position: absolute; + content: ""; + position: absolute; } .audio:not(:checked) + label:before, .audio:checked + label:before { - background: #ffffff; - border-radius: 6px; - height: 20px; - left:0; - top: -1px; - -webkit-transition: background-color .2s; - -moz-transition: background-color .2s; - -ms-transition: background-color .2s; - -o-transition: background-color .2s; - transition: background-color .2s; - width: 45px; + background: #ffffff; + border-radius: 6px; + height: 20px; + left: 0; + top: -1px; + -webkit-transition: background-color 0.2s; + -moz-transition: background-color 0.2s; + -ms-transition: background-color 0.2s; + -o-transition: background-color 0.2s; + transition: background-color 0.2s; + width: 45px; } .audio:not(:checked) + label:after, .audio:checked + label:after { - background: #3a3a3a; - border-radius: 6px; - height: 16px; - left: 2px; - top: 1px; - -webkit-transition: all .2s; - -moz-transition: all .2s; - -ms-transition: all .2s; - -o-transition: all .2s; - transition: all .2s; - width: 16px; + background: #3a3a3a; + border-radius: 6px; + height: 16px; + left: 2px; + top: 1px; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + -ms-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s; + width: 16px; } .audio:focus + label { - color:black; + color: black; } /* on checked */ .audio:checked + label:before { - background:#f2084a; + background: #f2084a; } .audio:checked + label:after { - background: #fff; - left: 27px; - top: 1px; + background: #fff; + left: 27px; + top: 1px; } .audio:checked + label .ui, .audio:not(:checked) + label .ui:before, .audio:checked + label .ui:after { - border-radius: 15px; - font-size: 11px; - font-weight: bold; - height:20px; - left: 3px; - line-height: 17px; - position: absolute; - -webkit-transition: all .2s; - -moz-transition: all .2s; - -ms-transition: all .2s; - -o-transition: all .2s; - transition: all .2s; - width: 45px; + border-radius: 15px; + font-size: 11px; + font-weight: bold; + height: 20px; + left: 3px; + line-height: 17px; + position: absolute; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + -ms-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s; + width: 45px; } .audio:not(:checked) + label .ui:before { - background-image:url('../img/mp3hover.png'); - background-position:right top; - background-repeat:no-repeat; - content: "no"; - left: 0; - min-width:56px; - padding-left:23px; - padding-top:2px; - -webkit-transition: all .2s; - -moz-transition: all .2s; - -ms-transition: all .2s; - -o-transition: all .2s; - transition: all .2s; + background-image: url("../img/mp3hover.png"); + background-position: right top; + background-repeat: no-repeat; + content: "no"; + left: 0; + min-width: 56px; + padding-left: 23px; + padding-top: 2px; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + -ms-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s; } .audio:checked + label .ui:after { - background-image:url('../img/mp3.png'); - background-position:right top; - background-repeat:no-repeat; - color: #fff; - content: "yes"; - -webkit-transition: all .2s; - -moz-transition: all .2s; - -ms-transition: all .2s; - -o-transition: all .2s; - transition: all .2s; - width:73px; + background-image: url("../img/mp3.png"); + background-position: right top; + background-repeat: no-repeat; + color: #fff; + content: "yes"; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + -ms-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s; + width: 73px; } .seekOptions { - display: none; - margin-top: 15px; - text-align: center; + display: none; + margin-top: 15px; + text-align: center; } .audio:checked ~ .seekOptions { - display: block; + display: block; } - /* Playlists */ .playlist-entry .thumb { - float: left; - margin-right: 1em; + float: left; + margin-right: 1em; } .playlist-entry { - clear: both; - padding-top: 2em; - text-align: left; - width: 600px; + clear: both; + padding-top: 2em; + text-align: left; + width: 600px; } .playlist-entry-title { - margin-top: 0; + margin-top: 0; } .playlist-entry-title a { - text-decoration: none; + text-decoration: none; } .playlist-entry-title a:hover { - text-decoration: underline; + text-decoration: underline; } .playlist-entry .downloadBtn { - border-width: 2px; - font-size: 16px; + border-width: 2px; + font-size: 16px; } - - - /* Supported websites list */ .logobis { - height:107px; - margin:0 auto 10px auto; - position:relative; - width:447px; + height: 107px; + margin: 0 auto 10px auto; + position: relative; + width: 447px; } - .logocompatible { - background-image:url('../img/logocompatible.png'); - background-position:0 0; - background-repeat:repeat-y; - display:block; - height:107px; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; - width:447px; + background-image: url("../img/logocompatible.png"); + background-position: 0 0; + background-repeat: repeat-y; + display: block; + height: 107px; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; + width: 447px; } .logocompatible:focus, .logocompatible:hover { - background-position:0 100%; - outline:none; - -webkit-transition: all 0.1s ease-in; - -moz-transition: all 0.1s ease-in; - -o-transition: all 0.1s ease-in; + background-position: 0 100%; + outline: none; + -webkit-transition: all 0.1s ease-in; + -moz-transition: all 0.1s ease-in; + -o-transition: all 0.1s ease-in; } - .logocompatiblemask { - background-image:url('../img/logocompatiblemask.png'); - background-position:0 100%; - background-repeat:no-repeat; - height:107px; - left:0; - position:absolute; - top:0; - width:447px; - z-index:10; + background-image: url("../img/logocompatiblemask.png"); + background-position: 0 100%; + background-repeat: no-repeat; + height: 107px; + left: 0; + position: absolute; + top: 0; + width: 447px; + z-index: 10; } .titre { - color:#383838; - font-family: 'Open Sans', sans-serif; - font-size:48px; - font-weight:300; + color: #383838; + font-family: "Open Sans", sans-serif; + font-size: 48px; + font-weight: 300; } .tripleliste { - margin-left:auto; - margin-right:auto; - margin-top:80px; - position:relative; - width:800px; + margin-left: auto; + margin-right: auto; + margin-top: 80px; + position: relative; + width: 800px; } - .tripleliste ul { - margin-bottom:1em; - margin-left:120px; - width:600px; + margin-bottom: 1em; + margin-left: 120px; + width: 600px; } .tripleliste ul li { - color:#383838; - float:left; - list-style-type:none; - position:relative; - text-align:left; - width:200px; + color: #383838; + float: left; + list-style-type: none; + position: relative; + text-align: left; + width: 200px; } html, body { - height:100%; - margin:0; + height: 100%; + margin: 0; } .wrapper { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - display:table; - height:100%; - margin:auto; - padding-bottom:110px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: table; + height: 100%; + margin: auto; + padding-bottom: 110px; } .main { - display:table-cell; - vertical-align:middle; + display: table-cell; + vertical-align: middle; } .extractors { - padding-top:60px; + padding-top: 60px; } .extractors .wrapper { - padding-bottom:5em; + padding-bottom: 5em; } .logocompatible, .social a { - font-size:0; - text-decoration:none; + font-size: 0; + text-decoration: none; } .social a { - color:#D1D1D1; + color: #d1d1d1; } .logocompatible { - color: #4F4F4F + color: #4f4f4f; } h1 { - margin:0; + margin: 0; } .error { - max-width: 100ex; + max-width: 100ex; } .error p { - text-align:justify; + text-align: justify; } .smaller { - font-size:smaller; + font-size: smaller; } .thumb { - max-width:700px; + max-width: 700px; } .format { - text-align:left; + text-align: left; } .best { - margin-bottom: 1em; + margin-bottom: 1em; } .monospace { - font-family:monospace; + font-family: monospace; } .customBitrate { - width: 6ex; + width: 6ex; } .locales { - float: left; - padding-left: 1em; - padding-top: 1em; - text-align: left; + float: left; + padding-left: 1em; + padding-top: 1em; + text-align: left; } .locales a, .locales a:visited { - color: #696969; - text-decoration: none; + color: #696969; + text-decoration: none; } .supportedLocales { - background-color: #fff; - list-style-type: none; - margin: 0; - opacity: 0; - padding-left: 0; - transition: visibility 0.5s; - visibility: hidden; + background-color: #fff; + list-style-type: none; + margin: 0; + opacity: 0; + padding-left: 0; + transition: visibility 0.5s; + visibility: hidden; } .supportedLocales li { - border-bottom: thin solid #E1E1E1; + border-bottom: thin solid #e1e1e1; } .supportedLocales li:last-child { - border-bottom: none; + border-bottom: none; } .supportedLocales li a { - display: block; - padding: 1em; - padding-right: 2em; + display: block; + padding: 1em; + padding-right: 2em; } .supportedLocales li:hover { - background-color: #cecece; + background-color: #cecece; } .localesBtn { - background-color: transparent; - border: none; - cursor: pointer; - display: inline-block; - padding: 1em; + background-color: transparent; + border: none; + cursor: pointer; + display: inline-block; + padding: 1em; } .localesBtn:focus { - background-color: #fff; - pointer-events: none; + background-color: #fff; + pointer-events: none; } .localesBtn:focus + .supportedLocales { - opacity: 1; - visibility: visible; + opacity: 1; + visibility: visible; } @media (max-width: 640px) { - .formats, - .thumb { - width:90%; - } + .formats, + .thumb { + width: 90%; + } - .URLinput{ - min-width:0; - } + .URLinput { + min-width: 0; + } - .logo { - max-width:330px; - } + .logo { + max-width: 330px; + } - .logocompatible, - .logocompatible img { - max-width:447px; - } + .logocompatible, + .logocompatible img { + max-width: 447px; + } - .logocompatible, - .logo, - .champs, - .URLinput, - .mp3 { - height:auto; - margin:auto; - width:90%; - } + .logocompatible, + .logo, + .champs, + .URLinput, + .mp3 { + height: auto; + margin: auto; + width: 90%; + } - .logo { - margin-top:50px; - } + .logo { + margin-top: 50px; + } - .logocompatible img { - height: auto; - width:100%; - } + .logocompatible img { + height: auto; + width: 100%; + } - .downloadBtn { - margin-top: 0.3em; - } - .mp3 { - margin-bottom: 1em; - } + .downloadBtn { + margin-top: 0.3em; + } + .mp3 { + margin-bottom: 1em; + } - footer { - display:none; - } + footer { + display: none; + } - .tripleliste ul, - .tripleliste { - margin-left:auto; - margin-top:auto; - width:auto; - } + .tripleliste ul, + .tripleliste { + margin-left: auto; + margin-top: auto; + width: auto; + } - .logocompatiblemask { - background:none; - } + .logocompatiblemask { + background: none; + } - .logocompatible { - background-color:#4F4F4F; - background-image:none; - height:auto; - } + .logocompatible { + background-color: #4f4f4f; + background-image: none; + height: auto; + } - .logocompatiblemask, - .logobis { - width:auto; - } + .logocompatiblemask, + .logobis { + width: auto; + } - .logocompatiblemask { - position:static; - } + .logocompatiblemask { + position: static; + } - .logobis { - height:auto; - } + .logobis { + height: auto; + } - .titre { - margin:auto; - } + .titre { + margin: auto; + } - .error p { - padding:0.5em; - text-align:left; - } + .error p { + padding: 0.5em; + text-align: left; + } - .playlist-entry { - text-align: center; - width: auto; - } - - .playlist-entry .thumb { - float: none; - margin-right: 0; - } + .playlist-entry { + text-align: center; + width: auto; + } + .playlist-entry .thumb { + float: none; + margin-right: 0; + } } @media all and (display-mode: standalone) { - .bookmarklet_wrapper { - display: none; - } + .bookmarklet_wrapper { + display: none; + } +} + +/* Visually hidden, displays content only to screen-readers */ +.sr-only { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + white-space: nowrap; + width: 1px; } diff --git a/index.php b/index.php index a3d4a27..791649b 100644 --- a/index.php +++ b/index.php @@ -2,10 +2,11 @@ require_once __DIR__.'/vendor/autoload.php'; use Alltube\Config; +use Alltube\Controller\DownloadController; use Alltube\Controller\FrontController; +use Alltube\Controller\JsonController; use Alltube\LocaleManager; use Alltube\LocaleMiddleware; -use Alltube\PlaylistArchiveStream; use Alltube\UglyRouter; use Alltube\ViewFactory; use Slim\App; @@ -15,7 +16,9 @@ if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.ph die; } -stream_wrapper_register('playlist', PlaylistArchiveStream::class); +if (is_file(__DIR__.'/config/config.yml')) { + Config::setFile(__DIR__.'/config/config.yml'); +} $app = new App(); $container = $app->getContainer(); @@ -28,42 +31,54 @@ $container['view'] = ViewFactory::create($container); if (!class_exists('Locale')) { die('You need to install the intl extension for PHP.'); } -$container['locale'] = new LocaleManager($_COOKIE); +$container['locale'] = new LocaleManager(); $app->add(new LocaleMiddleware($container)); -$controller = new FrontController($container, null, $_COOKIE); +$frontController = new FrontController($container); +$jsonController = new JsonController($container); +$downloadController = new DownloadController($container); -$container['errorHandler'] = [$controller, 'error']; +$container['errorHandler'] = [$frontController, 'error']; $app->get( '/', - [$controller, 'index'] + [$frontController, 'index'] )->setName('index'); + $app->get( '/extractors', - [$controller, 'extractors'] + [$frontController, 'extractors'] )->setName('extractors'); + $app->any( - '/video', - [$controller, 'video'] -)->setName('video'); + '/info', + [$frontController, 'info'] +)->setName('info'); +// Legacy route. +$app->any('/video', [$frontController, 'info']); + $app->any( '/watch', - [$controller, 'video'] + [$frontController, 'video'] ); -$app->get( - '/redirect', - [$controller, 'redirect'] -)->setName('redirect'); -$app->get( - '/json', - [$controller, 'json'] -)->setName('json'); + +$app->any( + '/download', + [$downloadController, 'download'] +)->setName('download'); +// Legacy route. +$app->get('/redirect', [$downloadController, 'download']); + $app->get( '/locale/{locale}', - [$controller, 'locale'] + [$frontController, 'locale'] )->setName('locale'); +$app->get( + '/json', + [$jsonController, 'json'] +)->setName('json'); + try { $app->run(); } catch (SmartyException $e) { diff --git a/package.json b/package.json index 804e447..0e1760e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "alltube", "description": "HTML GUI for youtube-dl", - "version": "1.2.5", + "version": "2.0.0", "author": "Pierre Rudloff", "bugs": "https://github.com/Rudloff/alltube/issues", "dependencies": { @@ -21,6 +21,7 @@ "grunt-markdownlint": "~2.1.0", "grunt-phpcs": "~0.4.0", "grunt-phpdocumentor": "~0.4.1", + "grunt-phpstan": "~0.2.0", "grunt-phpunit": "~0.3.6" }, "homepage": "https://www.alltubedownload.net/", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..0f1af62 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + ignoreErrors: + # The Archive constructor messes up the output buffering. + - '#Alltube\\PlaylistArchiveStream::__construct\(\) does not call parent constructor from Barracuda\\ArchiveStream\\ZipArchive\.#' diff --git a/templates/inc/head.tpl b/templates/inc/head.tpl index b4a5cf1..c3ee074 100644 --- a/templates/inc/head.tpl +++ b/templates/inc/head.tpl @@ -1,25 +1,25 @@ {locale path="../i18n" domain="Alltube"} - + - - -{if isset($description)} - - - -{/if} - -{$config->appName}{if isset($title)} - {$title|escape}{/if} - - - - - - - - - - + + + {if isset($description)} + + + + {/if} + + {$config->appName}{if isset($title)} - {$title|escape}{/if} + + + + + + + + + + diff --git a/templates/inc/header.tpl b/templates/inc/header.tpl index bf476df..844757e 100644 --- a/templates/inc/header.tpl +++ b/templates/inc/header.tpl @@ -18,9 +18,12 @@ {/if}
- -
-
+ +
+
+ +
+
diff --git a/templates/index.tpl b/templates/index.tpl index d151acb..707c45d 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -3,7 +3,7 @@
-
+ @@ -14,7 +14,7 @@ required placeholder="http://example.com/video" /> {if $config->uglyUrls} - + {/if}
{if $config->convert} @@ -25,8 +25,8 @@ {t}Audio only (MP3){/t}
- {t}From{/t} - {t}to{/t} + +
@@ -36,7 +36,7 @@ {t}See all supported websites{/t}

{t}Drag this to your bookmarks bar:{/t}

- {t}Bookmarklet{/t} + {t}Bookmarklet{/t}
diff --git a/templates/video.tpl b/templates/info.tpl similarity index 84% rename from templates/video.tpl rename to templates/info.tpl index cdf4242..54847ef 100644 --- a/templates/video.tpl +++ b/templates/info.tpl @@ -18,16 +18,16 @@ {/if}
- + {if isset($video->formats)}

{if $config->uglyUrls} - + {/if}

+ {if $config->stream} + + +

+ {/if} {if $config->convertAdvanced} - {foreach $config->convertAdvancedFormats as $format} {/foreach} {t}with{/t} - - + + + {t}kbit/s audio{/t}

{/if}
diff --git a/templates/password.tpl b/templates/password.tpl index de412ba..3425f36 100644 --- a/templates/password.tpl +++ b/templates/password.tpl @@ -5,7 +5,8 @@

{t}This video is protected{/t}

{t}You need a password in order to download this video.{/t}

- + +

diff --git a/templates/playlist.tpl b/templates/playlist.tpl index 39617a0..4afa662 100644 --- a/templates/playlist.tpl +++ b/templates/playlist.tpl @@ -7,24 +7,28 @@ {$video->title}{/if}{t}:{/t}

{if $config->stream} - webpage_url}" class="downloadBtn">Download everything + webpage_url}" class="downloadBtn">Download everything {/if} -{foreach $video->entries as $video} +{foreach $video->entries as $entry}

- {if !isset($video->title) and $video->ie_key == YoutubePlaylist} - Playlist + {if !isset($entry->title)} + {if $entry->ie_key == YoutubePlaylist} + Playlist + {else} + Video + {/if} {else} - {$video->title} + {$entry->title} {/if}

- url}">{t}Download{/t} - url}">{t}More options{/t} + url}">{t}Download{/t} + url}">{t}More options{/t}
{/foreach} diff --git a/tests/BaseTest.php b/tests/BaseTest.php new file mode 100644 index 0000000..5499101 --- /dev/null +++ b/tests/BaseTest.php @@ -0,0 +1,47 @@ +getConfigFile()); + } + + /** + * Destroy properties after test. + */ + protected function tearDown() + { + Config::destroyInstance(); + } +} diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index b9223f6..6480521 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -6,12 +6,11 @@ namespace Alltube\Test; use Alltube\Config; -use PHPUnit\Framework\TestCase; /** * Unit tests for the Config class. */ -class ConfigTest extends TestCase +class ConfigTest extends BaseTest { /** * Config class instance. @@ -25,17 +24,9 @@ class ConfigTest extends TestCase */ protected function setUp() { - $this->config = Config::getInstance('config/config_test.yml'); - } + parent::setUp(); - /** - * Destroy variables created by setUp(). - * - * @return void - */ - protected function tearDown() - { - Config::destroyInstance(); + $this->config = Config::getInstance(); } /** @@ -45,8 +36,9 @@ class ConfigTest extends TestCase */ public function testGetInstance() { - $this->assertEquals($this->config->convert, false); - $this->assertConfig($this->config); + $config = Config::getInstance(); + $this->assertEquals($config->convert, false); + $this->assertConfig($config); } /** @@ -70,25 +62,77 @@ class ConfigTest extends TestCase } /** - * Test the getInstance function with a missing config file. + * Test the setFile function. + * + * @return void + */ + public function testSetFile() + { + Config::setFile($this->getConfigFile()); + $this->assertConfig($this->config); + } + + /** + * Test the setFile function with a missing config file. * * @return void * @expectedException Exception */ - public function testGetInstanceWithMissingFile() + public function testSetFileWithMissingFile() { - Config::getInstance('foo'); + Config::setFile('foo'); } /** - * Test the getInstance function with an empty filename. + * Test the setOptions function. * * @return void */ - public function testGetInstanceWithEmptyFile() + public function testSetOptions() { - $config = Config::getInstance(''); - $this->assertConfig($config); + Config::setOptions(['appName' => 'foo']); + $config = Config::getInstance(); + $this->assertEquals($config->appName, 'foo'); + } + + /** + * Test the setOptions function. + * + * @return void + */ + public function testSetOptionsWithoutUpdate() + { + if (getenv('APPVEYOR')) { + $this->markTestSkipped( + "This will fail on AppVeyor because it won't be able to find youtube-dl at the defaut path." + ); + } + + Config::setOptions(['appName' => 'foo'], false); + $config = Config::getInstance(); + $this->assertEquals($config->appName, 'foo'); + } + + /** + * Test the setOptions function. + * + * @return void + * @expectedException Exception + */ + public function testSetOptionsWithBadYoutubedl() + { + Config::setOptions(['youtubedl' => 'foo']); + } + + /** + * Test the setOptions function. + * + * @return void + * @expectedException Exception + */ + public function testSetOptionsWithBadPython() + { + Config::setOptions(['python' => 'foo']); } /** @@ -100,11 +144,9 @@ class ConfigTest extends TestCase { Config::destroyInstance(); putenv('CONVERT=1'); - putenv('PYTHON=foo'); - $config = Config::getInstance('config/config_test.yml'); + Config::setFile($this->getConfigFile()); + $config = Config::getInstance(); $this->assertEquals($config->convert, true); - $this->assertEquals($config->python, 'foo'); putenv('CONVERT'); - putenv('PYTHON'); } } diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php new file mode 100644 index 0000000..d4a94f9 --- /dev/null +++ b/tests/ControllerTest.php @@ -0,0 +1,143 @@ +container = new Container(); + $this->request = Request::createFromEnvironment(Environment::mock()); + $this->response = new Response(); + $this->container['view'] = ViewFactory::create($this->container, $this->request); + $this->container['locale'] = new LocaleManager(); + + $frontController = new FrontController($this->container); + $downloadController = new DownloadController($this->container); + + $this->container['router']->map(['GET'], '/', [$frontController, 'index']) + ->setName('index'); + $this->container['router']->map(['GET'], '/video', [$frontController, 'info']) + ->setName('info'); + $this->container['router']->map(['GET'], '/extractors', [$frontController, 'extractors']) + ->setName('extractors'); + $this->container['router']->map(['GET'], '/locale', [$frontController, 'locale']) + ->setName('locale'); + $this->container['router']->map(['GET'], '/redirect', [$downloadController, 'download']) + ->setName('download'); + } + + /** + * Run controller function with custom query parameters and return the result. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return Response HTTP response + */ + protected function getRequestResult($request, array $params) + { + return $this->controller->$request( + $this->request->withQueryParams($params), + $this->response + ); + } + + /** + * Assert that calling controller function with these parameters returns a 200 HTTP response. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsOk($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isOk()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP redirect. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsRedirect($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isRedirect()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP 500 error. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsServerError($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isServerError()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP 400 error. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsClientError($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isClientError()); + } +} diff --git a/tests/ConvertedPlaylistArchiveStreamTest.php b/tests/ConvertedPlaylistArchiveStreamTest.php new file mode 100644 index 0000000..48a7d9e --- /dev/null +++ b/tests/ConvertedPlaylistArchiveStreamTest.php @@ -0,0 +1,27 @@ +stream = new ConvertedPlaylistArchiveStream($video); + } +} diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php new file mode 100644 index 0000000..df89efa --- /dev/null +++ b/tests/DownloadControllerTest.php @@ -0,0 +1,221 @@ +controller = new DownloadController($this->container); + } + + /** + * Test the download() function without the URL parameter. + * + * @return void + */ + public function testDownloadWithoutUrl() + { + $this->assertRequestIsRedirect('download'); + } + + /** + * Test the download() function. + * + * @return void + */ + public function testDownload() + { + $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); + } + + /** + * Test the download() function with a specific format. + * + * @return void + */ + public function testDownloadWithFormat() + { + $this->assertRequestIsRedirect( + 'download', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst'] + ); + } + + /** + * Test the download() function with streams enabled. + * + * @return void + */ + public function testDownloadWithStream() + { + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'stream' => true] + ); + } + + /** + * Test the download() function with an M3U stream. + * + * @return void + */ + public function testDownloadWithM3uStream() + { + if (getenv('CI')) { + $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.'); + } + + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', + 'format' => 'hls-2176', + 'stream' => true, + ] + ); + } + + /** + * Test the download() function with an RTMP stream. + * + * @return void + */ + public function testDownloadWithRtmpStream() + { + $this->markTestIncomplete('We need to find another RTMP video.'); + + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'] + ); + } + + /** + * Test the download() function with a remuxed video. + * + * @return void + */ + public function testDownloadWithRemux() + { + Config::setOptions(['remux' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'bestvideo+bestaudio', + ] + ); + } + + /** + * Test the download() function with a remuxed video but remux disabled. + * + * @return void + */ + public function testDownloadWithRemuxDisabled() + { + $this->assertRequestIsServerError( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'bestvideo+bestaudio', + ] + ); + } + + /** + * Test the download() function with a missing password. + * + * @return void + */ + public function testDownloadWithMissingPassword() + { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']); + } + + /** + * Test the download() function with an error. + * + * @return void + */ + public function testDownloadWithError() + { + $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']); + } + + /** + * Test the download() function with an video that returns an empty URL. + * This can be caused by trying to redirect to a playlist. + * + * @return void + */ + public function testDownloadWithEmptyUrl() + { + $this->assertRequestIsServerError( + 'download', + ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] + ); + } + + /** + * Test the download() function with a playlist stream. + * + * @return void + * @requires OS Linux + */ + public function testDownloadWithPlaylist() + { + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] + ); + } + + /** + * Test the download() function with an advanced conversion. + * + * @return void + */ + public function testDownloadWithAdvancedConversion() + { + Config::setOptions(['convertAdvanced' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'best', + 'customConvert' => 'on', + 'customBitrate' => 32, + 'customFormat' => 'flv', + ] + ); + } +} diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 42b9d19..1dabf40 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -7,171 +7,23 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\Controller\FrontController; -use Alltube\LocaleManager; -use Alltube\ViewFactory; use Exception; -use PHPUnit\Framework\TestCase; -use Slim\Container; use Slim\Http\Environment; use Slim\Http\Request; -use Slim\Http\Response; /** * Unit tests for the FrontController class. */ -class FrontControllerTest extends TestCase +class FrontControllerTest extends ControllerTest { - /** - * Slim dependency container. - * - * @var Container - */ - private $container; - - /** - * Mock HTTP request. - * - * @var Request - */ - private $request; - - /** - * Mock HTTP response. - * - * @var Response - */ - private $response; - - /** - * FrontController instance used in tests. - * - * @var FrontController - */ - private $controller; - - /** - * Config class instance. - * - * @var Config - */ - private $config; - /** * Prepare tests. */ protected function setUp() { - $this->container = new Container(); - $this->request = Request::createFromEnvironment(Environment::mock()); - $this->response = new Response(); - $this->container['view'] = ViewFactory::create($this->container, $this->request); - $this->container['locale'] = new LocaleManager(); + parent::setUp(); - if (PHP_OS == 'WINNT') { - $configFile = 'config_test_windows.yml'; - } else { - $configFile = 'config_test.yml'; - } - $this->config = Config::getInstance('config/'.$configFile); - $this->controller = new FrontController($this->container, $this->config); - - $this->container['router']->map(['GET'], '/', [$this->controller, 'index']) - ->setName('index'); - $this->container['router']->map(['GET'], '/video', [$this->controller, 'video']) - ->setName('video'); - $this->container['router']->map(['GET'], '/extractors', [$this->controller, 'extractors']) - ->setName('extractors'); - $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'redirect']) - ->setName('redirect'); - $this->container['router']->map(['GET'], '/locale', [$this->controller, 'locale']) - ->setName('locale'); - } - - /** - * Destroy properties after test. - */ - protected function tearDown() - { - Config::destroyInstance(); - } - - /** - * Run controller function with custom query parameters and return the result. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * @param Config $config Custom config - * - * @return Response HTTP response - */ - private function getRequestResult($request, array $params, Config $config = null) - { - if (isset($config)) { - $controller = new FrontController($this->container, $config); - } else { - $controller = $this->controller; - } - - return $controller->$request( - $this->request->withQueryParams($params), - $this->response - ); - } - - /** - * Assert that calling controller function with these parameters returns a 200 HTTP response. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * @param Config $config Custom config - * - * @return void - */ - private function assertRequestIsOk($request, array $params = [], Config $config = null) - { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isOk()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP redirect. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * @param Config $config Custom config - * - * @return void - */ - private function assertRequestIsRedirect($request, array $params = [], Config $config = null) - { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isRedirect()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP 500 error. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * @param Config $config Custom config - * - * @return void - */ - private function assertRequestIsServerError($request, array $params = [], Config $config = null) - { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isServerError()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP 400 error. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * @param Config $config Custom config - * - * @return void - */ - private function assertRequestIsClientError($request, array $params = [], Config $config = null) - { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isClientError()); + $this->controller = new FrontController($this->container); } /** @@ -181,20 +33,7 @@ class FrontControllerTest extends TestCase */ public function testConstructor() { - $controller = new FrontController($this->container, $this->config); - $this->assertInstanceOf(FrontController::class, $controller); - } - - /** - * Test the constructor with a default config. - * - * @return void - * @requires OS Linux - */ - public function testConstructorWithDefaultConfig() - { - $controller = new FrontController($this->container); - $this->assertInstanceOf(FrontController::class, $controller); + $this->assertInstanceOf(FrontController::class, new FrontController($this->container)); } /** @@ -204,9 +43,8 @@ class FrontControllerTest extends TestCase */ public function testConstructorWithStream() { - $this->config->stream = true; - $controller = new FrontController($this->container, $this->config); - $this->assertInstanceOf(FrontController::class, $controller); + Config::setOptions(['stream' => true]); + $this->assertInstanceOf(FrontController::class, new FrontController($this->container)); } /** @@ -256,55 +94,67 @@ class FrontControllerTest extends TestCase } /** - * Test the video() function without the url parameter. + * Test the info() function without the url parameter. * * @return void */ - public function testVideoWithoutUrl() + public function testInfoWithoutUrl() { - $this->assertRequestIsRedirect('video'); + $this->assertRequestIsRedirect('info'); } /** - * Test the video() function. + * Test the info() function. * * @return void */ - public function testVideo() + public function testInfo() { - $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); + $this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); } /** - * Test the video() function with audio conversion. + * Test the info() function with audio conversion. * * @return void */ - public function testVideoWithAudio() + public function testInfoWithAudio() { - $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]); - } + Config::setOptions(['convert' => true]); - /** - * Test the video() function with audio conversion from a Vimeo video. - * - * @return void - */ - public function testVideoWithVimeoAudio() - { - // So we can test the fallback to default format - $this->assertRequestIsOk('video', ['url' => 'https://vimeo.com/251997032', 'audio' => true]); - } - - /** - * Test the video() function with audio enabled and an URL that doesn't need to be converted. - * - * @return void - */ - public function testVideoWithUnconvertedAudio() - { $this->assertRequestIsRedirect( - 'video', + 'info', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true] + ); + } + + /** + * Test the info() function with audio conversion from a Vimeo video. + * + * @return void + */ + public function testInfoWithVimeoAudio() + { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + Config::setOptions(['convert' => true]); + + // So we can test the fallback to default format + $this->assertRequestIsRedirect('info', ['url' => 'https://vimeo.com/251997032', 'audio' => true]); + } + + /** + * Test the info() function with audio enabled and an URL that doesn't need to be converted. + * + * @return void + */ + public function testInfoWithUnconvertedAudio() + { + Config::setOptions(['convert' => true]); + + $this->assertRequestIsRedirect( + 'info', [ 'url' => 'https://2080.bandcamp.com/track/cygnus-x-the-orange-theme-2080-faulty-chip-cover', 'audio' => true, @@ -313,13 +163,16 @@ class FrontControllerTest extends TestCase } /** - * Test the video() function with a password. + * Test the info() function with a password. * * @return void */ - public function testVideoWithPassword() + public function testInfoWithPassword() { - $result = $this->controller->video( + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + $result = $this->controller->info( $this->request->withQueryParams(['url' => 'http://vimeo.com/68375962']) ->withParsedBody(['password' => 'youtube-dl']), $this->response @@ -328,41 +181,44 @@ class FrontControllerTest extends TestCase } /** - * Test the video() function with a missing password. + * Test the info() function with a missing password. * * @return void */ - public function testVideoWithMissingPassword() + public function testInfoWithMissingPassword() { - $this->assertRequestIsOk('video', ['url' => 'http://vimeo.com/68375962']); - $this->assertRequestIsOk('video', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962']); + $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); } /** - * Test the video() function with streams enabled. + * Test the info() function with streams enabled. * * @return void */ - public function testVideoWithStream() + public function testInfoWithStream() { - $this->config->stream = true; - $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'], $this->config); + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); $this->assertRequestIsOk( - 'video', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true], - $this->config + 'info', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true] ); } /** - * Test the video() function with a playlist. + * Test the info() function with a playlist. * * @return void */ - public function testVideoWithPlaylist() + public function testInfoWithPlaylist() { $this->assertRequestIsOk( - 'video', + 'info', ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] ); } @@ -378,224 +234,6 @@ class FrontControllerTest extends TestCase $this->assertTrue($result->isServerError()); } - /** - * Test the redirect() function without the URL parameter. - * - * @return void - */ - public function testRedirectWithoutUrl() - { - $this->assertRequestIsRedirect('redirect'); - } - - /** - * Test the redirect() function. - * - * @return void - */ - public function testRedirect() - { - $this->assertRequestIsRedirect('redirect', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); - } - - /** - * Test the redirect() function with a specific format. - * - * @return void - */ - public function testRedirectWithFormat() - { - $this->assertRequestIsRedirect( - 'redirect', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst'] - ); - } - - /** - * Test the redirect() function with streams enabled. - * - * @return void - */ - public function testRedirectWithStream() - { - $this->config->stream = true; - $this->assertRequestIsOk( - 'redirect', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'], - $this->config - ); - } - - /** - * Test the redirect() function with an M3U stream. - * - * @return void - */ - public function testRedirectWithM3uStream() - { - $this->config->stream = true; - $this->assertRequestIsOk( - 'redirect', - [ - 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', - 'format' => 'hls-2176', - ], - $this->config - ); - } - - /** - * Test the redirect() function with an RTMP stream. - * - * @return void - */ - public function testRedirectWithRtmpStream() - { - $this->markTestIncomplete('We need to find another RTMP video.'); - - $this->config->stream = true; - $this->assertRequestIsOk( - 'redirect', - ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'], - $this->config - ); - } - - /** - * Test the redirect() function with a remuxed video. - * - * @return void - */ - public function testRedirectWithRemux() - { - $this->config->remux = true; - $this->assertRequestIsOk( - 'redirect', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'bestvideo+bestaudio', - ], - $this->config - ); - } - - /** - * Test the redirect() function with a remuxed video but remux disabled. - * - * @return void - */ - public function testRedirectWithRemuxDisabled() - { - $this->assertRequestIsServerError( - 'redirect', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'bestvideo+bestaudio', - ] - ); - } - - /** - * Test the redirect() function with a missing password. - * - * @return void - */ - public function testRedirectWithMissingPassword() - { - $this->assertRequestIsRedirect('redirect', ['url' => 'http://vimeo.com/68375962']); - } - - /** - * Test the redirect() function with an error. - * - * @return void - */ - public function testRedirectWithError() - { - $this->assertRequestIsServerError('redirect', ['url' => 'http://example.com/foo']); - } - - /** - * Test the redirect() function with an video that returns an empty URL. - * This can be caused by trying to redirect to a playlist. - * - * @return void - */ - public function testRedirectWithEmptyUrl() - { - $this->assertRequestIsServerError( - 'redirect', - ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] - ); - } - - /** - * Test the redirect() function with a playlist stream. - * - * @return void - * @requires OS Linux - */ - public function testRedirectWithPlaylist() - { - $this->config->stream = true; - $this->assertRequestIsOk( - 'redirect', - ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'], - $this->config - ); - } - - /** - * Test the redirect() function with an advanced conversion. - * - * @return void - */ - public function testRedirectWithAdvancedConversion() - { - $this->config->convertAdvanced = true; - $this->assertRequestIsOk( - 'redirect', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'best', - 'customConvert' => 'on', - 'customBitrate' => 32, - 'customFormat' => 'flv', - ], - $this->config - ); - } - - /** - * Test the json() function without the URL parameter. - * - * @return void - */ - public function testJsonWithoutUrl() - { - $this->assertRequestIsClientError('json'); - } - - /** - * Test the json() function. - * - * @return void - */ - public function testJson() - { - $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); - } - - /** - * Test the json() function with an error. - * - * @return void - */ - public function testJsonWithError() - { - $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']); - } - /** * Test the locale() function. * diff --git a/tests/JsonControllerTest.php b/tests/JsonControllerTest.php new file mode 100644 index 0000000..caefccb --- /dev/null +++ b/tests/JsonControllerTest.php @@ -0,0 +1,54 @@ +controller = new JsonController($this->container); + } + + /** + * Test the json() function. + * + * @return void + */ + public function testJson() + { + $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); + } + + /** + * Test the json() function with an error. + * + * @return void + */ + public function testJsonWithError() + { + $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']); + } + + /** + * Test the json() function without the URL parameter. + * + * @return void + */ + public function testJsonWithoutUrl() + { + $this->assertRequestIsClientError('json'); + } +} diff --git a/tests/LocaleManagerTest.php b/tests/LocaleManagerTest.php index 2fff818..6ebb1b2 100644 --- a/tests/LocaleManagerTest.php +++ b/tests/LocaleManagerTest.php @@ -7,12 +7,11 @@ namespace Alltube\Test; use Alltube\Locale; use Alltube\LocaleManager; -use PHPUnit\Framework\TestCase; /** - * Unit tests for the Config class. + * Unit tests for the LocaleManagerTest class. */ -class LocaleManagerTest extends TestCase +class LocaleManagerTest extends BaseTest { /** * LocaleManager class instance. @@ -26,19 +25,18 @@ class LocaleManagerTest extends TestCase */ protected function setUp() { - $this->localeManager = new LocaleManager(); $_SESSION[LocaleManager::class]['locale'] = 'foo_BAR'; + $this->localeManager = new LocaleManager(); } /** - * Test the getSupportedLocales function. + * Unset locale after each test. * * @return void */ - public function testConstructorWithCookies() + protected function tearDown() { - $localeManager = new LocaleManager([]); - $this->assertEquals('foo_BAR', (string) $localeManager->getLocale()); + $this->localeManager->unsetLocale(); } /** diff --git a/tests/LocaleMiddlewareTest.php b/tests/LocaleMiddlewareTest.php index f7455fd..0be9f43 100644 --- a/tests/LocaleMiddlewareTest.php +++ b/tests/LocaleMiddlewareTest.php @@ -8,7 +8,6 @@ namespace Alltube\Test; use Alltube\Locale; use Alltube\LocaleManager; use Alltube\LocaleMiddleware; -use PHPUnit\Framework\TestCase; use Slim\Container; use Slim\Http\Environment; use Slim\Http\Request; @@ -17,7 +16,7 @@ use Slim\Http\Response; /** * Unit tests for the FrontController class. */ -class LocaleMiddlewareTest extends TestCase +class LocaleMiddlewareTest extends BaseTest { /** * LocaleMiddleware instance. diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php index 3651b22..6e429fd 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -6,12 +6,11 @@ namespace Alltube\Test; use Alltube\Locale; -use PHPUnit\Framework\TestCase; /** - * Unit tests for the Config class. + * Unit tests for the LocaleTest class. */ -class LocaleTest extends TestCase +class LocaleTest extends BaseTest { /** * Locale class instance. @@ -77,4 +76,14 @@ class LocaleTest extends TestCase { $this->assertEquals('fr', $this->localeObject->getIso3166()); } + + /** + * Test the getCountry function. + * + * @return void + */ + public function testGetCountry() + { + $this->assertEquals(country('fr'), $this->localeObject->getCountry()); + } } diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index fde76c6..dfb2af7 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -5,112 +5,23 @@ namespace Alltube\Test; -use Alltube\Config; -use Alltube\PlaylistArchiveStream; -use PHPUnit\Framework\TestCase; +use Alltube\Stream\PlaylistArchiveStream; +use Alltube\Video; /** - * Unit tests for the ViewFactory class. + * Unit tests for the PlaylistArchiveStream class. */ -class PlaylistArchiveStreamTest extends TestCase +class PlaylistArchiveStreamTest extends StreamTest { - /** - * PlaylistArchiveStream instance. - * - * @var PlaylistArchiveStream - */ - private $stream; - /** * Prepare tests. */ protected function setUp() { - if (PHP_OS == 'WINNT') { - $configFile = 'config_test_windows.yml'; - } else { - $configFile = 'config_test.yml'; - } - $this->stream = new PlaylistArchiveStream(Config::getInstance('config/'.$configFile)); - } + parent::setUp(); - /** - * Test the stream_open() function. - * - * @return void - */ - public function testStreamOpen() - { - $this->assertTrue($this->stream->stream_open('playlist://foo')); - } + $video = new Video('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); - /** - * Test the stream_write() function. - * - * @return void - */ - public function testStreamWrite() - { - $this->assertEquals(0, $this->stream->stream_write()); - } - - /** - * Test the stream_stat() function. - * - * @return void - */ - public function testStreamStat() - { - $this->assertEquals(['mode' => 4096], $this->stream->stream_stat()); - } - - /** - * Test the stream_tell() function. - * - * @return void - */ - public function testStreamTell() - { - $this->stream->stream_open('playlist://foo'); - $this->assertInternalType('int', $this->stream->stream_tell()); - } - - /** - * Test the stream_seek() function. - * - * @return void - */ - public function testStreamSeek() - { - $this->stream->stream_open('playlist://foo'); - $this->assertInternalType('bool', $this->stream->stream_seek(3)); - } - - /** - * Test the stream_read() function. - * - * @return void - */ - public function testStreamRead() - { - $this->stream->stream_open('playlist://BaW_jenozKc;BaW_jenozKc/worst'); - while (!$this->stream->stream_eof()) { - $result = $this->stream->stream_read(8192); - $this->assertInternalType('string', $result); - if (is_string($result)) { - $this->assertLessThanOrEqual(8192, strlen($result)); - } - } - } - - /** - * Test the stream_eof() function. - * - * @return void - */ - public function testStreamEof() - { - $this->stream->stream_open('playlist://foo'); - $this->assertFalse($this->stream->stream_eof()); + $this->stream = new PlaylistArchiveStream($video); } } diff --git a/tests/StreamTest.php b/tests/StreamTest.php new file mode 100644 index 0000000..d74af82 --- /dev/null +++ b/tests/StreamTest.php @@ -0,0 +1,222 @@ +stream->close(); + } + + /** + * Test the write() function. + * + * @return void + */ + public function testWrite() + { + if ($this->stream->isWritable()) { + $this->assertNull($this->stream->write('foo')); + } else { + $this->expectException(RuntimeException::class); + $this->stream->write('foo'); + } + } + + /** + * Test the tell() function. + * + * @return void + */ + public function testTell() + { + $this->assertInternalType('int', $this->stream->tell()); + } + + /** + * Test the seek() function. + * + * @return void + */ + public function testSeek() + { + if ($this->stream->isSeekable()) { + if ($this->stream->isWritable()) { + // We might need some data. + $this->stream->write('foobar'); + } + + $this->stream->seek(3); + $this->assertEquals(3, $this->stream->tell()); + } else { + $this->expectException(RuntimeException::class); + $this->stream->seek(3); + } + } + + /** + * Test the read() function. + * + * @return void + */ + public function testRead() + { + $result = $this->stream->read(8192); + $this->assertInternalType('string', $result); + $this->assertLessThanOrEqual(8192, strlen($result)); + } + + /** + * Test the read() function. + * + * @return void + */ + public function testReadEntireStream() + { + $this->markTestIncomplete('Can we test the whole logic without reading the whole stream?'); + } + + /** + * Test the eof() function. + * + * @return void + */ + public function testEof() + { + $this->assertFalse($this->stream->eof()); + } + + /** + * Test the getSize() function. + * + * @return void + */ + public function testGetSize() + { + $this->assertNull($this->stream->getSize()); + } + + /** + * Test the isSeekable() function. + * + * @return void + */ + public function testIsSeekable() + { + $this->assertInternalType('boolean', $this->stream->isSeekable()); + } + + /** + * Test the rewind() function. + * + * @return void + */ + public function testRewind() + { + if ($this->stream->isSeekable()) { + if ($this->stream->isWritable()) { + // We might need some data. + $this->stream->write('foobar'); + } + + $this->stream->rewind(); + $this->assertEquals(0, $this->stream->tell()); + } else { + $this->expectException(RuntimeException::class); + $this->stream->rewind(); + } + } + + /** + * Test the isWritable() function. + * + * @return void + */ + public function testIsWritable() + { + $this->assertInternalType('boolean', $this->stream->isWritable()); + } + + /** + * Test the isReadable() function. + * + * @return void + */ + public function testIsReadable() + { + $this->assertTrue($this->stream->isReadable()); + } + + /** + * Test the getContents() function. + * + * @return void + */ + public function testGetContents() + { + $this->assertInternalType('string', $this->stream->getContents()); + } + + /** + * Test the getMetadata() function. + * + * @return void + */ + public function testGetMetadata() + { + $this->assertInternalType('array', $this->stream->getMetadata()); + } + + /** + * Test the getMetadata() function. + * + * @return void + */ + public function testGetMetadataWithKey() + { + $this->assertInternalType('string', $this->stream->getMetadata('stream_type')); + $this->assertInternalType('string', $this->stream->getMetadata('mode')); + $this->assertInternalType('boolean', $this->stream->getMetadata('seekable')); + $this->assertNull($this->stream->getMetadata('foo')); + } + + /** + * Test the detach() function. + * + * @return void + */ + public function testDetach() + { + $this->assertInternalType('resource', $this->stream->detach()); + } + + /** + * Test the __toString() function. + * + * @return void + */ + public function testToString() + { + $this->assertInternalType('string', $this->stream->__toString()); + $this->assertInternalType('string', (string) $this->stream); + } +} diff --git a/tests/UglyRouterTest.php b/tests/UglyRouterTest.php index 541b78d..3609db8 100644 --- a/tests/UglyRouterTest.php +++ b/tests/UglyRouterTest.php @@ -6,14 +6,13 @@ namespace Alltube\Test; use Alltube\UglyRouter; -use PHPUnit\Framework\TestCase; use Slim\Http\Environment; use Slim\Http\Request; /** * Unit tests for the UglyRouter class. */ -class UglyRouterTest extends TestCase +class UglyRouterTest extends BaseTest { /** * UglyRouter instance. diff --git a/tests/VideoDownloadStubsTest.php b/tests/VideoDownloadStubsTest.php deleted file mode 100644 index 71a50ad..0000000 --- a/tests/VideoDownloadStubsTest.php +++ /dev/null @@ -1,138 +0,0 @@ -config = Config::getInstance('config/'.$configFile); - $this->download = new VideoDownload($this->config); - $this->url = 'https://www.youtube.com/watch?v=XJC9_JkzugE'; - } - - /** - * Remove stubs. - * - * @return void - */ - protected function tearDown() - { - Mockery::close(); - } - - /** - * Test getAudioStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetAudioStreamWithPopenError() - { - $this->download->getAudioStream($this->url, 'best'); - } - - /** - * Test getM3uStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetM3uStreamWithPopenError() - { - $this->download->getM3uStream($this->download->getJSON($this->url, 'best')); - } - - /** - * Test getRtmpStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetRtmpStreamWithPopenError() - { - $this->download->getRtmpStream($this->download->getJSON($this->url, 'best')); - } - - /** - * Test getRemuxStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetRemuxStreamWithPopenError() - { - $this->download->getRemuxStream([$this->url, $this->url]); - } - - /** - * Test getPlaylistArchiveStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetPlaylistArchiveStreamWithPopenError() - { - $video = $this->download->getJSON( - 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC', - 'best' - ); - $this->download->getPlaylistArchiveStream($video, 'best'); - } - - /** - * Test getConvertedStream function with a buggy popen. - * - * @return void - * @expectedException Exception - */ - public function testGetConvertedStreamWithPopenError() - { - $this->download->getConvertedStream($this->url, 'best', 32, 'flv'); - } -} diff --git a/tests/VideoStubsTest.php b/tests/VideoStubsTest.php new file mode 100644 index 0000000..0b96139 --- /dev/null +++ b/tests/VideoStubsTest.php @@ -0,0 +1,103 @@ +video = new Video('https://www.youtube.com/watch?v=XJC9_JkzugE'); + } + + /** + * Remove stubs. + * + * @return void + */ + protected function tearDown() + { + Mockery::close(); + } + + /** + * Test getAudioStream function with a buggy popen. + * + * @return void + * @expectedException Exception + */ + public function testGetAudioStreamWithPopenError() + { + $this->video->getAudioStream(); + } + + /** + * Test getM3uStream function with a buggy popen. + * + * @return void + * @expectedException Exception + */ + public function testGetM3uStreamWithPopenError() + { + $this->video->getM3uStream(); + } + + /** + * Test getRtmpStream function with a buggy popen. + * + * @return void + * @expectedException Exception + */ + public function testGetRtmpStreamWithPopenError() + { + $this->video->getRtmpStream(); + } + + /** + * Test getRemuxStream function with a buggy popen. + * + * @return void + * @expectedException Exception + */ + public function testGetRemuxStreamWithPopenError() + { + $video = $this->video->withFormat('bestvideo+bestaudio'); + $video->getRemuxStream(); + } + + /** + * Test getConvertedStream function with a buggy popen. + * + * @return void + * @expectedException Exception + */ + public function testGetConvertedStreamWithPopenError() + { + $this->video->getConvertedStream(32, 'flv'); + } +} diff --git a/tests/VideoDownloadTest.php b/tests/VideoTest.php similarity index 63% rename from tests/VideoDownloadTest.php rename to tests/VideoTest.php index f93a249..c726fd4 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoTest.php @@ -1,92 +1,30 @@ config = Config::getInstance('config/'.$configFile); - $this->download = new VideoDownload($this->config); - } - - /** - * Destroy properties after test. - */ - protected function tearDown() - { - Config::destroyInstance(); - } - - /** - * Test VideoDownload constructor with wrong youtube-dl path. - * - * @return void - * @expectedException Exception - */ - public function testConstructorWithMissingYoutubedl() - { - $this->config->youtubedl = 'foo'; - new VideoDownload($this->config); - } - - /** - * Test VideoDownload constructor with wrong Python path. - * - * @return void - * @expectedException Exception - */ - public function testConstructorWithMissingPython() - { - $this->config->python = 'foo'; - new VideoDownload($this->config); - } - - /** - * Test listExtractors function. + * Test getExtractors function. * * @return void */ - public function testListExtractors() + public function testGetExtractors() { - $extractors = $this->download->listExtractors(); - $this->assertContains('youtube', $extractors); + $this->assertContains('youtube', Video::getExtractors()); } /** - * Test getURL function. + * Test getUrl function. * * @param string $url URL * @param string $format Format @@ -99,52 +37,70 @@ class VideoDownloadTest extends TestCase * @dataProvider m3uUrlProvider * @dataProvider remuxUrlProvider */ - public function testGetURL( + public function testgetUrl( $url, $format, /* @scrutinizer ignore-unused */ $filename, /* @scrutinizer ignore-unused */ $extension, $domain ) { - $videoURL = $this->download->getURL($url, $format); - $this->assertContains($domain, $videoURL[0]); + $video = new Video($url, $format); + foreach ($video->getUrl() as $videoURL) { + $this->assertContains($domain, $videoURL); + } } /** - * Test getURL function with a protected video. + * Test getUrl function with a protected video. * * @return void */ - public function testGetURLWithPassword() + public function testgetUrlWithPassword() { - $videoURL = $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl'); - $this->assertContains('vimeocdn.com', $videoURL[0]); + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + + $video = new Video('http://vimeo.com/68375962', 'best', 'youtube-dl'); + foreach ($video->getUrl() as $videoURL) { + $this->assertContains('vimeocdn.com', $videoURL); + } } /** - * Test getURL function with a protected video and no password. + * Test getUrl function with a protected video and no password. * * @return void - * @expectedException Alltube\PasswordException + * @expectedException Alltube\Exception\PasswordException */ - public function testGetURLWithMissingPassword() + public function testgetUrlWithMissingPassword() { - $this->download->getURL('http://vimeo.com/68375962'); + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + + $video = new Video('http://vimeo.com/68375962'); + $video->getUrl(); } /** - * Test getURL function with a protected video and a wrong password. + * Test getUrl function with a protected video and a wrong password. * * @return void * @expectedException Exception */ - public function testGetURLWithWrongPassword() + public function testgetUrlWithWrongPassword() { - $this->download->getURL('http://vimeo.com/68375962', null, 'foo'); + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + + $video = new Video('http://vimeo.com/68375962', 'best', 'foo'); + $video->getUrl(); } /** - * Test getURL function errors. + * Test getUrl function errors. * * @param string $url URL * @@ -152,9 +108,10 @@ class VideoDownloadTest extends TestCase * @expectedException Exception * @dataProvider ErrorUrlProvider */ - public function testGetURLError($url) + public function testgetUrlError($url) { - $this->download->getURL($url); + $video = new Video($url); + $video->getUrl(); } /** @@ -164,7 +121,7 @@ class VideoDownloadTest extends TestCase */ public function urlProvider() { - return [ + $videos = [ [ 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'best[protocol^=http]', 'It_s_Not_Me_It_s_You_-_Hearts_Under_Fire-M7IpKCZ47pU', @@ -178,12 +135,6 @@ class VideoDownloadTest extends TestCase 'mp4', 'googlevideo.com', ], - [ - 'https://vimeo.com/24195442', 'best[protocol^=http]', - 'Carving_the_Mountains-24195442', - 'mp4', - 'vimeocdn.com', - ], [ 'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best', 'Kaleidoscope_Leonard_Cohen-b039d07m', @@ -203,6 +154,18 @@ class VideoDownloadTest extends TestCase 'openload.co', ], ]; + + if (!getenv('CI')) { + // Travis is blacklisted by Vimeo. + $videos[] = [ + 'https://vimeo.com/24195442', 'best[protocol^=http]', + 'Carving_the_Mountains-24195442', + 'mp4', + 'vimeocdn.com', + ]; + } + + return $videos; } /** @@ -229,14 +192,19 @@ class VideoDownloadTest extends TestCase */ public function m3uUrlProvider() { - return [ - [ + $videos = []; + + if (!getenv('CI')) { + // Twitter returns a 429 error when the test is ran too many times. + $videos[] = [ 'https://twitter.com/verge/status/813055465324056576/video/1', 'hls-2176', 'The_Verge_-_This_tiny_origami_robot_can_self-fold_and_complete_tasks-813055465324056576', 'mp4', 'video.twimg.com', - ], - ]; + ]; + } + + return $videos; } /** @@ -278,9 +246,10 @@ class VideoDownloadTest extends TestCase * @dataProvider urlProvider * @dataProvider m3uUrlProvider */ - public function testGetJSON($url, $format) + public function testGetJson($url, $format) { - $info = $this->download->getJSON($url, $format); + $video = new Video($url, $format); + $info = $video->getJson(); $this->assertObjectHasAttribute('webpage_url', $info); $this->assertObjectHasAttribute('url', $info); $this->assertObjectHasAttribute('ext', $info); @@ -298,9 +267,10 @@ class VideoDownloadTest extends TestCase * @expectedException Exception * @dataProvider ErrorURLProvider */ - public function testGetJSONError($url) + public function testGetJsonError($url) { - $this->download->getJSON($url); + $video = new Video($url); + $video->getJson(); } /** @@ -318,8 +288,8 @@ class VideoDownloadTest extends TestCase */ public function testGetFilename($url, $format, $filename, $extension) { - $videoFilename = $this->download->getFilename($url, $format); - $this->assertEquals($videoFilename, $filename.'.'.$extension); + $video = new Video($url, $format); + $this->assertEquals($video->getFilename(), $filename.'.'.$extension); } /** @@ -333,25 +303,8 @@ class VideoDownloadTest extends TestCase */ public function testGetFilenameError($url) { - $this->download->getFilename($url); - } - - /** - * Test getAudioFilename function. - * - * @param string $url URL - * @param string $format Format - * @param string $filename Filename - * - * @return void - * @dataProvider urlProvider - * @dataProvider m3uUrlProvider - * @dataProvider remuxUrlProvider - */ - public function testGetAudioFilename($url, $format, $filename) - { - $videoFilename = $this->download->getAudioFilename($url, $format); - $this->assertEquals($videoFilename, $filename.'.mp3'); + $video = new Video($url); + $video->getFilename(); } /** @@ -365,9 +318,8 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStream($url, $format) { - $stream = $this->download->getAudioStream($url, $format); - $this->assertInternalType('resource', $stream); - $this->assertFalse(feof($stream)); + $video = new Video($url, $format); + $this->assertStream($video->getAudioStream()); } /** @@ -382,9 +334,10 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStreamAvconvError($url, $format) { - $this->config->avconv = 'foobar'; - $download = new VideoDownload($this->config); - $download->getAudioStream($url, $format); + Config::setOptions(['avconv' => 'foobar']); + + $video = new Video($url, $format); + $video->getAudioStream(); } /** @@ -399,7 +352,8 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStreamM3uError($url, $format) { - $this->download->getAudioStream($url, $format); + $video = new Video($url, $format); + $video->getAudioStream(); } /** @@ -410,7 +364,12 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStreamDashError() { - $this->download->getAudioStream('https://vimeo.com/251997032', 'bestaudio/best'); + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + + $video = new Video('https://vimeo.com/251997032', 'bestaudio/best'); + $video->getAudioStream(); } /** @@ -421,10 +380,11 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStreamPlaylistError() { - $this->download->getAudioStream( + $video = new Video( 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC', 'best' ); + $video->getAudioStream(); } /** @@ -451,11 +411,8 @@ class VideoDownloadTest extends TestCase */ public function testGetM3uStream($url, $format) { - $this->assertStream( - $this->download->getM3uStream( - $this->download->getJSON($url, $format) - ) - ); + $video = new Video($url, $format); + $this->assertStream($video->getM3uStream()); } /** @@ -469,10 +426,24 @@ class VideoDownloadTest extends TestCase */ public function testGetRemuxStream($url, $format) { - $urls = $this->download->getURL($url, $format); - if (count($urls) > 1) { - $this->assertStream($this->download->getRemuxStream($urls)); - } + $video = new Video($url, $format); + $this->assertStream($video->getRemuxStream()); + } + + /** + * Test getRemuxStream function with a video with only one URL. + * + * @param string $url URL + * @param string $format Format + * + * @return void + * @dataProvider urlProvider + * @expectedException Exception + */ + public function testGetRemuxStreamWithWrongVideo($url, $format) + { + $video = new Video($url, $format); + $video->getRemuxStream(); } /** @@ -488,11 +459,9 @@ class VideoDownloadTest extends TestCase { $this->markTestIncomplete('We need to find another RTMP video.'); - $this->assertStream( - $this->download->getRtmpStream( - $this->download->getJSON($url, $format) - ) - ); + $video = new Video($url, $format); + + $this->assertStream($video->getRtmpStream()); } /** @@ -507,25 +476,10 @@ class VideoDownloadTest extends TestCase */ public function testGetM3uStreamAvconvError($url, $format) { - $this->config->avconv = 'foobar'; - $download = new VideoDownload($this->config); - $video = $download->getJSON($url, $format); - $download->getM3uStream($video); - } + Config::setOptions(['avconv' => 'foobar']); - /** - * Test getPlaylistArchiveStream function. - * - * @return void - * @requires OS Linux - */ - public function testGetPlaylistArchiveStream() - { - $video = $this->download->getJSON( - 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC', - 'best' - ); - $this->assertStream($this->download->getPlaylistArchiveStream($video, 'best')); + $video = new Video($url, $format); + $video->getM3uStream(); } /** @@ -539,7 +493,8 @@ class VideoDownloadTest extends TestCase */ public function testGetConvertedStream($url, $format) { - $this->assertStream($this->download->getConvertedStream($url, $format, 32, 'flv')); + $video = new Video($url, $format); + $this->assertStream($video->getConvertedStream(32, 'flv')); } /** @@ -554,6 +509,7 @@ class VideoDownloadTest extends TestCase */ public function testGetConvertedStreamM3uError($url, $format) { - $this->download->getConvertedStream($url, $format, 32, 'flv'); + $video = new Video($url, $format); + $video->getConvertedStream(32, 'flv'); } } diff --git a/tests/ViewFactoryTest.php b/tests/ViewFactoryTest.php index 285340f..c73a3b4 100644 --- a/tests/ViewFactoryTest.php +++ b/tests/ViewFactoryTest.php @@ -6,7 +6,6 @@ namespace Alltube\Test; use Alltube\ViewFactory; -use PHPUnit\Framework\TestCase; use Slim\Container; use Slim\Http\Environment; use Slim\Http\Request; @@ -15,7 +14,7 @@ use Slim\Views\Smarty; /** * Unit tests for the ViewFactory class. */ -class ViewFactoryTest extends TestCase +class ViewFactoryTest extends BaseTest { /** * Test the create() function. diff --git a/tests/YoutubeChunkStreamTest.php b/tests/YoutubeChunkStreamTest.php new file mode 100644 index 0000000..e386bd2 --- /dev/null +++ b/tests/YoutubeChunkStreamTest.php @@ -0,0 +1,27 @@ +stream = new YoutubeChunkStream($video->getHttpResponse()); + } +} diff --git a/tests/YoutubeStreamTest.php b/tests/YoutubeStreamTest.php new file mode 100644 index 0000000..7cc7580 --- /dev/null +++ b/tests/YoutubeStreamTest.php @@ -0,0 +1,47 @@ +stream = new YoutubeStream($video); + } + + /** + * Test the getMetadata() function. + * + * @return void + */ + public function testGetMetadataWithKey() + { + $this->assertNull($this->stream->getMetadata('foo')); + } + + /** + * Test the detach() function. + * + * @return void + */ + public function testDetach() + { + $this->assertNull($this->stream->detach()); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4f64daf..60c3ae8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,7 +2,6 @@ /** * File used to bootstrap tests. */ -use Alltube\PlaylistArchiveStream; use phpmock\mockery\PHPMockery; /** @@ -14,8 +13,6 @@ ini_set('session.use_cookies', 0); session_cache_limiter(''); session_start(); -stream_wrapper_register('playlist', PlaylistArchiveStream::class); - /* * @see https://bugs.php.net/bug.php?id=68541 */ diff --git a/yarn.lock b/yarn.lock index e0e5bad..0f68ccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,23 +5,27 @@ "JSV@>= 4.0.x": version "4.0.2" resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" + integrity sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c= abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" +ajv@^6.5.5: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" + fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" alce@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/alce/-/alce-1.0.0.tgz#426184c98ee288d0eeac77fd63fed680b667cab6" + integrity sha1-QmGEyY7iiNDurHf9Y/7WgLZnyrY= dependencies: esprima "~1.0.4" estraverse "~1.3.0" @@ -29,32 +33,39 @@ alce@1.0.0: ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" + integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg= aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== archiver-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174" + integrity sha1-5QtMCccL89aA4y/xt5lOn52JUXQ= dependencies: glob "^7.0.0" graceful-fs "^4.1.0" @@ -66,6 +77,7 @@ archiver-utils@^1.3.0: archiver@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/archiver/-/archiver-1.3.0.tgz#4f2194d6d8f99df3f531e6881f14f15d55faaf22" + integrity sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI= dependencies: archiver-utils "^1.3.0" async "^2.0.0" @@ -80,63 +92,83 @@ archiver@^1.3.0: are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== dependencies: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.2, argparse@^1.0.7: +argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= async@^1.5.0, async@~1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= async@^2.0.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== dependencies: - lodash "^4.17.10" + lodash "^4.17.11" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= -aws4@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" + integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" @@ -144,16 +176,19 @@ bl@^1.0.0: block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= dependencies: inherits "~2.0.0" bluebird@^3.0.5: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + version "3.5.4" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" + integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw== body-parser@~1.14.0: version "1.14.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.14.2.tgz#1015cb1fe2c443858259581db53332f8d0cf50f9" + integrity sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk= dependencies: bytes "2.2.0" content-type "~1.0.1" @@ -169,6 +204,7 @@ body-parser@~1.14.0: brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -176,10 +212,12 @@ brace-expansion@^1.1.7: buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== -buffer-alloc@^1.1.0: +buffer-alloc@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== dependencies: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" @@ -187,30 +225,40 @@ buffer-alloc@^1.1.0: buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= buffer-shims@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + integrity sha1-mXjOMXOIxkmth5MCjDR37wRKi1E= -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" bytes@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588" + integrity sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg= bytes@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + integrity sha1-fZcZb51br39pNeJZhVSe3SpsIzk= camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" map-obj "^1.0.0" @@ -218,14 +266,17 @@ camelcase-keys@^2.0.0: camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^1.0.0, chalk@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -236,72 +287,81 @@ chalk@^1.0.0, chalk@^1.1.1: chalk@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" + integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8= dependencies: ansi-styles "~1.0.0" has-color "~0.1.0" strip-ansi "~0.1.0" chalk@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== clean-css@~4.1.1: version "4.1.11" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a" + integrity sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo= dependencies: source-map "0.5.x" clone@~2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= coffeescript@~1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.10.0.tgz#e7aa8301917ef621b35d8a39f348dcdd1db7e33e" + integrity sha1-56qDAZF+9iGzXYo580jc3R234z4= color-convert@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: - color-name "^1.1.1" + color-name "1.1.3" -color-name@^1.1.1: +color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= colors@*: - version "1.3.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.0.tgz#5f20c9fef6945cb1134260aab33bfbdc8295e04e" + version "1.3.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" + integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= -combined-stream@1.0.6, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== dependencies: delayed-stream "~1.0.0" compress-commons@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.2.tgz#524a9f10903f3a813389b0225d27c48bb751890f" + integrity sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8= dependencies: buffer-crc32 "^0.2.1" crc32-stream "^2.0.0" @@ -311,33 +371,42 @@ compress-commons@^1.2.0: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= content-type@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= crc32-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4" + integrity sha1-483TtN8xaN10494/u8t7KX/pCPQ= dependencies: crc "^3.4.4" readable-stream "^2.0.0" crc@^3.4.4: - version "3.5.0" - resolved "https://registry.yarnpkg.com/crc/-/crc-3.5.0.tgz#98b8ba7d489665ba3979f59b21381374101a1964" + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" csslint@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/csslint/-/csslint-1.0.5.tgz#19cc3eda322160fd3f7232af1cb2a360e898a2e9" + integrity sha1-Gcw+2jIhYP0/cjKvHLKjYOiYouk= dependencies: clone "~2.1.0" parserlib "~1.1.1" @@ -345,18 +414,21 @@ csslint@^1.0.0: currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" dateformat@~1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= dependencies: get-stdin "^4.0.1" meow "^3.3.0" @@ -364,138 +436,170 @@ dateformat@~1.0.12: debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= dependencies: ms "0.7.1" decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-extend@~0.2.5: version "0.2.11" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.2.11.tgz#7a16ba69729132340506170494bc83f7076fe08f" + integrity sha1-eha6aXKRMjQFBhcElLyD9wdv4I8= delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= depd@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= detect-libc@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-0.2.0.tgz#47fdf567348a17ec25fcbf0b9e446348a76f9fb5" + integrity sha1-R/31ZzSKF+wl/L8LnkRjSKdvn7U= detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" + safer-buffer "^2.1.0" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== dependencies: once "^1.4.0" entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== error-ex@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -esprima@^2.6.0: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esprima@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= estraverse@~1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.3.2.tgz#37c2b893ef13d723f276d878d60d8535152a6c42" + integrity sha1-N8K4k+8T1yPydth41g2FNRUqbEI= eventemitter2@~0.4.13: version "0.4.14" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas= exit@~0.1.1, exit@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-template@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.1.tgz#981f188c0c3a87d2e28f559bc541426ff94f21dd" + integrity sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg== extend-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/extend-object/-/extend-object-1.0.0.tgz#42514f84015d1356caf5187969dfb2bc1bda0823" + integrity sha1-QlFPhAFdE1bK9Rh5ad+yvBvaCCM= -extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= faye-websocket@~0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= dependencies: websocket-driver ">=0.5.1" figures@^1.0.1: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" @@ -503,6 +607,7 @@ figures@^1.0.1: find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -510,12 +615,14 @@ find-up@^1.0.0: findup-sync@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" + integrity sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY= dependencies: glob "~5.0.0" fixpack@~2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/fixpack/-/fixpack-2.3.1.tgz#53f03d88aab7d5123259282f0088a9a3b19836c2" + integrity sha1-U/A9iKq31RIyWSgvAIipo7GYNsI= dependencies: alce "1.0.0" colors "*" @@ -525,26 +632,31 @@ fixpack@~2.3.0: forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" - combined-stream "1.0.6" + combined-stream "^1.0.6" mime-types "^2.1.12" fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fstream@^1.0.0, fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -554,6 +666,7 @@ fstream@^1.0.0, fstream@^1.0.2: gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -567,34 +680,41 @@ gauge@~2.7.3: gaze@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== dependencies: globule "^1.0.0" get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= getobject@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/getobject/-/getobject-0.1.0.tgz#047a449789fa160d018f5486ed91320b6ec7885c" + integrity sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw= getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-rev-2@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/git-rev-2/-/git-rev-2-0.1.0.tgz#748165a8c84fb01ea67467ff15ab3e37ace4064c" + integrity sha1-dIFlqMhPsB6mdGf/Fas+N6zkBkw= github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" +glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@~7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -606,6 +726,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: glob@~5.0.0: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= dependencies: inflight "^1.0.4" inherits "2" @@ -616,6 +737,7 @@ glob@~5.0.0: glob@~7.0.0: version "7.0.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" + integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo= dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -627,18 +749,21 @@ glob@~7.0.0: globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" + integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ== dependencies: glob "~7.1.1" lodash "~4.17.10" minimatch "~3.0.2" graceful-fs@^4.1.0, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== grunt-cli@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/grunt-cli/-/grunt-cli-1.2.0.tgz#562b119ebb069ddb464ace2845501be97b35b6a8" + integrity sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg= dependencies: findup-sync "~0.3.0" grunt-known-options "~1.1.0" @@ -648,6 +773,7 @@ grunt-cli@~1.2.0: grunt-contrib-compress@~1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/grunt-contrib-compress/-/grunt-contrib-compress-1.4.3.tgz#01ceffb9c637f52e7081f463750983d0a3b0fa73" + integrity sha1-Ac7/ucY39S5wgfRjdQmD0KOw+nM= dependencies: archiver "^1.3.0" chalk "^1.1.1" @@ -660,6 +786,7 @@ grunt-contrib-compress@~1.4.1: grunt-contrib-csslint@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/grunt-contrib-csslint/-/grunt-contrib-csslint-2.0.0.tgz#3129d94dfe507357f23337d24ae9e9aa4b9d57df" + integrity sha1-MSnZTf5Qc1fyMzfSSunpqkudV98= dependencies: chalk "^1.0.0" csslint "^1.0.0" @@ -669,6 +796,7 @@ grunt-contrib-csslint@~2.0.0: grunt-contrib-cssmin@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/grunt-contrib-cssmin/-/grunt-contrib-cssmin-2.2.1.tgz#64cbebe60134bc1270ca4154514ec4007cc16f7f" + integrity sha512-IXNomhQ5ekVZbDbj/ik5YccoD9khU6LT2fDXqO1+/Txjq8cp0tQKjVS8i8EAbHOrSDkL7/UD6A7b+xj98gqh9w== dependencies: chalk "^1.0.0" clean-css "~4.1.1" @@ -677,6 +805,7 @@ grunt-contrib-cssmin@~2.2.1: grunt-contrib-watch@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/grunt-contrib-watch/-/grunt-contrib-watch-1.0.1.tgz#ca65934b6e04dbd26da684d598f79ee22f47fdac" + integrity sha512-8Zka/svGl6+ZwF7d6z/CfXwsb4cDODnajmZsY4nUAs9Ob0kJEcsLiDf5qm2HdDoEcm3NHjWCrFiWx+PZ2y4D7A== dependencies: async "^1.5.0" gaze "^1.1.0" @@ -686,12 +815,14 @@ grunt-contrib-watch@~1.0.0: grunt-fixpack@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/grunt-fixpack/-/grunt-fixpack-0.1.0.tgz#e29553e7535b3d3ffcef6ba556ed5c9684ccace0" + integrity sha1-4pVT51NbPT/872ulVu1cloTMrOA= dependencies: fixpack "~2.3.0" grunt-githash@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/grunt-githash/-/grunt-githash-0.1.3.tgz#17c7bc75129b678fb187a4fcff83552e40489b56" + integrity sha1-F8e8dRKbZ4+xh6T8/4NVLkBIm1Y= dependencies: bluebird "^3.0.5" git-rev-2 "^0.1.0" @@ -699,23 +830,27 @@ grunt-githash@~0.1.3: grunt-jslint@~1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/grunt-jslint/-/grunt-jslint-1.1.15.tgz#c7d7dbb850307d9e9bd12ea62fef7ed6ac2abe7f" + integrity sha512-8r75ufVi049gYXl6WRTEoswEOnDTMGZH7vVIeSdmY6ODDCAOeYflB6QtRwhbiUYBEiqVfLFN5hzj8ZNBMnFrMw== dependencies: jslint "^0.10.3" grunt-jsonlint@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/grunt-jsonlint/-/grunt-jsonlint-1.1.0.tgz#a31ee97240aee3f343ca263c45bd532063127db2" + integrity sha1-ox7pckCu4/NDyiY8Rb1TIGMSfbI= dependencies: jsonlint "1.6.2" strip-json-comments "^2.0.0" grunt-known-options@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-1.1.0.tgz#a4274eeb32fa765da5a7a3b1712617ce3b144149" + version "1.1.1" + resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-1.1.1.tgz#6cc088107bd0219dc5d3e57d91923f469059804d" + integrity sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ== grunt-legacy-log-utils@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz#d2f442c7c0150065d9004b08fd7410d37519194e" + integrity sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA== dependencies: chalk "~2.4.1" lodash "~4.17.10" @@ -723,6 +858,7 @@ grunt-legacy-log-utils@~2.0.0: grunt-legacy-log@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz#c8cd2c6c81a4465b9bbf2d874d963fef7a59ffb9" + integrity sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw== dependencies: colors "~1.1.2" grunt-legacy-log-utils "~2.0.0" @@ -732,6 +868,7 @@ grunt-legacy-log@~2.0.0: grunt-legacy-util@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz#e10624e7c86034e5b870c8a8616743f0a0845e42" + integrity sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A== dependencies: async "~1.5.2" exit "~0.1.1" @@ -744,30 +881,41 @@ grunt-legacy-util@~1.1.1: grunt-markdownlint@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/grunt-markdownlint/-/grunt-markdownlint-2.1.0.tgz#8d9b1abeebb588893576c40221acd7c4098e1f37" + integrity sha1-jZsavuu1iIk1dsQCIazXxAmOHzc= dependencies: markdownlint "^0.11.0" grunt-phpcs@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/grunt-phpcs/-/grunt-phpcs-0.4.0.tgz#a08d625fc64465e453b2bd93f810b2a81e94bdaa" + integrity sha1-oI1iX8ZEZeRTsr2T+BCyqB6Uvao= grunt-phpdocumentor@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/grunt-phpdocumentor/-/grunt-phpdocumentor-0.4.1.tgz#513a5cefd8804d45792ea35ddfbcb2471e307141" + integrity sha1-UTpc79iATUV5LqNd37yyRx4wcUE= + +grunt-phpstan@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/grunt-phpstan/-/grunt-phpstan-0.2.0.tgz#9c5942a58b225f507ee14912abeb05b9182c5e99" + integrity sha512-cbxChIghG6wkzaHZH0YYg7NbYCQzqfmpZrCN43paF3nQzBLUnWtrUXCJi9BwMtM+AG2yzfcih9+G+Mrm9B+Xrw== grunt-phpunit@~0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/grunt-phpunit/-/grunt-phpunit-0.3.6.tgz#0e75bee6b5c2e65fda45075672a06ceb2cecd869" + integrity sha1-DnW+5rXC5l/aRQdWcqBs6yzs2Gk= grunt-potomo@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/grunt-potomo/-/grunt-potomo-3.5.0.tgz#5ad8c6f7e763ad5b51839e5c6d32358062c73795" + integrity sha1-WtjG9+djrVtRg55cbTI1gGLHN5U= dependencies: shelljs "~0.7.5" grunt@~1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/grunt/-/grunt-1.0.3.tgz#b3c99260c51d1b42835766e796527b60f7bba374" + version "1.0.4" + resolved "https://registry.yarnpkg.com/grunt/-/grunt-1.0.4.tgz#c799883945a53a3d07622e0737c8f70bfe19eb38" + integrity sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ== dependencies: coffeescript "~1.10.0" dateformat "~1.0.12" @@ -780,7 +928,7 @@ grunt@~1.0.1: grunt-legacy-log "~2.0.0" grunt-legacy-util "~1.1.1" iconv-lite "~0.4.13" - js-yaml "~3.5.2" + js-yaml "~3.13.0" minimatch "~3.0.2" mkdirp "~0.5.1" nopt "~3.0.6" @@ -790,60 +938,72 @@ grunt@~1.0.1: gzip-size@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520" + integrity sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA= dependencies: duplexer "^0.1.1" har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== dependencies: - ajv "^5.1.0" + ajv "^6.5.5" har-schema "^2.0.0" has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-color@~0.1.0: version "0.1.7" resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8= has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= hooker@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" + integrity sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk= hosted-git-info@^2.1.4: - version "2.6.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" + integrity sha1-GX4izevUGYWF6GlO9nhhl7ke2UI= dependencies: inherits "~2.0.1" statuses "1" http-parser-js@>=0.4.0: - version "0.4.13" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.13.tgz#3bd6d6fde6e3172c9334c3b33b6c193d80fe1137" + version "0.5.0" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8" + integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w== http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -852,16 +1012,24 @@ http-signature@~1.2.0: iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" + integrity sha1-H4irpKsLFQjoMSrMOTRfNumS4vI= iconv-lite@~0.4.13: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + iltorb@^1.0.13: version "1.3.10" resolved "https://registry.yarnpkg.com/iltorb/-/iltorb-1.3.10.tgz#a0d9e4e7d52bf510741442236cbe0cc4230fc9f8" + integrity sha512-nyB4+ru1u8CQqQ6w7YjasboKN3NQTN8GH/V/eEssNRKhW6UbdxdWhB9fJ5EEdjJfezKY0qPrcwLyIcgjL8hHxA== dependencies: detect-libc "^0.2.0" nan "^2.6.2" @@ -871,12 +1039,14 @@ iltorb@^1.0.13: indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" @@ -884,96 +1054,109 @@ inflight@^1.0.4: inherits@2, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== interpret@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + version "1.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" + integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -js-yaml@~3.5.2: - version "3.5.5" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.5.5.tgz#0377c38017cabc7322b0d1fbcd25a491641f2fbe" +js-yaml@~3.13.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: - argparse "^1.0.2" - esprima "^2.6.0" + argparse "^1.0.7" + esprima "^4.0.0" jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jslint@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/jslint/-/jslint-0.10.3.tgz#890da3e79932edf06c5f4b52a7b5ba6436867436" + integrity sha1-iQ2j55ky7fBsX0tSp7W6ZDaGdDY= dependencies: exit "~0.1.2" glob "^7.0.3" nopt "~3.0.1" readable-stream "~2.1.2" -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= jsonlint@1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/jsonlint/-/jsonlint-1.6.2.tgz#5737045085f55eb455c68b1ff4ebc01bd50e8830" + integrity sha1-VzcEUIX1XrRVxosf9OvAG9UOiDA= dependencies: JSV ">= 4.0.x" nomnom ">= 1.5.x" @@ -981,6 +1164,7 @@ jsonlint@1.6.2: jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" @@ -990,22 +1174,26 @@ jsprim@^1.2.2: lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= dependencies: readable-stream "^2.0.5" linkify-it@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + version "2.1.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.1.0.tgz#c4caf38a6cd7ac2212ef3c7d2bde30a91561f9db" + integrity sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg== dependencies: uc.micro "^1.0.1" livereload-js@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.3.0.tgz#c3ab22e8aaf5bf3505d80d098cbad67726548c9a" + version "2.4.0" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" + integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -1013,13 +1201,15 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -lodash@^4.0.0, lodash@^4.17.10, lodash@^4.7.0, lodash@^4.8.0, lodash@^4.8.2, lodash@~4.17.10, lodash@~4.17.5: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" +lodash@^4.0.0, lodash@^4.17.11, lodash@^4.7.0, lodash@^4.8.0, lodash@^4.8.2, lodash@~4.17.10, lodash@~4.17.5: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" signal-exit "^3.0.0" @@ -1027,10 +1217,12 @@ loud-rejection@^1.0.0: map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= markdown-it@8.4.2: version "8.4.2" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== dependencies: argparse "^1.0.7" entities "~1.1.1" @@ -1041,12 +1233,14 @@ markdown-it@8.4.2: markdownlint@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.11.0.tgz#3858bbdbc1ab78abf0c098d841c72b63dd3206a0" + integrity sha512-wE5WdKD6zW2DQaPQ5TFBTXh5j76DnWd/IFffnDQgHmi6Y61DJXBDfLftZ/suJHuv6cwPjM6gKw2GaRLJMOR+Mg== dependencies: markdown-it "8.4.2" maxmin@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/maxmin/-/maxmin-2.1.0.tgz#4d3b220903d95eee7eb7ac7fa864e72dc09a3166" + integrity sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY= dependencies: chalk "^1.0.0" figures "^1.0.1" @@ -1056,14 +1250,17 @@ maxmin@^2.1.0: mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" decamelize "^1.1.2" @@ -1076,71 +1273,82 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" +mime-db@~1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.39.0.tgz#f95a20275742f7d2ad0429acfe40f4233543780e" + integrity sha512-DTsrw/iWVvwHH+9Otxccdyy0Tgiil6TWK/xhfARJZF/QFhwOgZgOIvA2/VIGpM8U7Q8z5nDmdDWC6tuVMJNibw== -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" +mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19: + version "2.1.23" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.23.tgz#d4eacd87de99348a6858fe1e479aad877388d977" + integrity sha512-ROk/m+gMVSrRxTkMlaQOvFmFmYDc7sZgrjjM76abqmd2Cc5fCV7jAMA5XUccEtJ3cYiYdgixUVI+fApc2LkXlw== dependencies: - mime-db "~1.33.0" + mime-db "~1.39.0" mimic-response@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== "minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= minimist@~0.0.7: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= nan@^2.6.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== node-abi@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.1.tgz#7628c4d4ec4e9cd3764ceb3652f36b2e7f8d4923" + version "2.7.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643" + integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw== dependencies: semver "^5.4.1" node-gyp@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== dependencies: fstream "^1.0.0" glob "^7.0.3" graceful-fs "^4.1.2" - minimatch "^3.0.2" mkdirp "^0.5.0" nopt "2 || 3" npmlog "0 || 1 || 2 || 3 || 4" osenv "0" - request "2" + request "^2.87.0" rimraf "2" semver "~5.3.0" tar "^2.0.0" @@ -1149,6 +1357,7 @@ node-gyp@^3.6.2: "nomnom@>= 1.5.x": version "1.8.1" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" + integrity sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc= dependencies: chalk "~0.4.0" underscore "~1.6.0" @@ -1156,31 +1365,36 @@ node-gyp@^3.6.2: noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= "nopt@2 || 3", nopt@~3.0.1, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= dependencies: abbrev "1" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" + resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" normalize-path@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.1: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -1190,42 +1404,51 @@ normalize-path@^2.0.0: number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" open-sans-fontface@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/open-sans-fontface/-/open-sans-fontface-1.4.0.tgz#03cc6d1bf5e6a8b5b47910888562f722c5dd3428" + integrity sha1-A8xtG/XmqLW0eRCIhWL3IsXdNCg= os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@0: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -1233,34 +1456,41 @@ osenv@0: parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parserlib@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/parserlib/-/parserlib-1.1.1.tgz#a64cfa724062434fdfc351c9a4ec2d92b94c06f4" + integrity sha1-pkz6ckBiQ0/fw1HJpOwtkrlMBvQ= parseurl@~1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-is-absolute@^1.0.0, path-is-absolute@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -1269,24 +1499,29 @@ path-type@^1.0.0: performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= prebuild-install@^2.3.0: version "2.5.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" + integrity sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g== dependencies: detect-libc "^1.0.3" expand-template "^1.0.2" @@ -1307,24 +1542,34 @@ prebuild-install@^2.3.0: pretty-bytes@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf" + integrity sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8= dependencies: number-is-nan "^1.0.0" pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" + integrity sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk= process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +psl@^1.1.24: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== pump@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -1332,6 +1577,7 @@ pump@^1.0.0: pump@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -1339,22 +1585,32 @@ pump@^2.0.1: punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" + integrity sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4= qs@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" + integrity sha1-TZMuXH6kEcynajEtOaYGIA/VDNk= -qs@~6.5.1: +qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== raw-body@~2.1.5: version "2.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" + integrity sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q= dependencies: bytes "2.4.0" iconv-lite "0.4.13" @@ -1363,6 +1619,7 @@ raw-body@~2.1.5: rc@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rc/-/rc-0.6.0.tgz#e1c930059af831c85413fe275ae2f40f4e3c5371" + integrity sha1-4ckwBZr4MchUE/4nWuL0D048U3E= dependencies: deep-extend "~0.2.5" ini "~1.3.0" @@ -1372,6 +1629,7 @@ rc@^0.6.0: rc@^1.1.6: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" ini "~1.3.0" @@ -1381,6 +1639,7 @@ rc@^1.1.6: read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -1388,6 +1647,7 @@ read-pkg-up@^1.0.1: read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" @@ -1396,6 +1656,7 @@ read-pkg@^1.0.0: readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -1408,6 +1669,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable readable-stream@~2.1.2: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + integrity sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA= dependencies: buffer-shims "^1.0.0" core-util-is "~1.0.0" @@ -1420,12 +1682,14 @@ readable-stream@~2.1.2: rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" @@ -1433,77 +1697,89 @@ redent@^1.0.0: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" -request@2: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" +request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: aws-sign2 "~0.7.0" - aws4 "^1.6.0" + aws4 "^1.8.0" caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" + combined-stream "~1.0.6" + extend "~3.0.2" forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" + form-data "~2.3.2" + har-validator "~5.1.0" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" + mime-types "~2.1.19" + oauth-sign "~0.9.0" performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" - uuid "^3.1.0" + uuid "^3.3.2" -resolve@^1.1.6: - version "1.7.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" +resolve@^1.1.6, resolve@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== dependencies: - path-parse "^1.0.5" + path-parse "^1.0.6" resolve@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= rimraf@2, rimraf@~2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: - glob "^7.0.5" + glob "^7.1.3" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== "semver@2 || 3 || 4 || 5", semver@^5.4.1: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= shelljs@~0.7.5: version "0.7.8" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -1512,14 +1788,17 @@ shelljs@~0.7.5: signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= simple-concat@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= simple-get@^2.7.0: version "2.8.1" resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== dependencies: decompress-response "^3.3.0" once "^1.3.1" @@ -1528,62 +1807,73 @@ simple-get@^2.7.0: source-map@0.5.x: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= spdx-correct@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== spdx-expression-parse@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== sprintf-js@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" ecc-jsbn "~0.1.1" + getpass "^0.1.1" jsbn "~0.1.0" + safer-buffer "^2.0.2" tweetnacl "~0.14.0" statuses@1: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stream-buffers@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -1592,6 +1882,7 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" @@ -1599,62 +1890,74 @@ string-width@^1.0.1: string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-ansi@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" + integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE= strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@0.1.x: version "0.1.3" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-0.1.3.tgz#164c64e370a8a3cc00c9e01b539e569823f0ee54" + integrity sha1-Fkxk43Coo8wAyeAbU55WmCPw7lQ= strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^5.3.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" tar-fs@^1.13.0: - version "1.16.2" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.2.tgz#17e5239747e399f7e77344f5f53365f04af53577" + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== dependencies: chownr "^1.0.1" mkdirp "^0.5.1" @@ -1662,20 +1965,22 @@ tar-fs@^1.13.0: tar-stream "^1.1.2" tar-stream@^1.1.2, tar-stream@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395" + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== dependencies: bl "^1.0.0" - buffer-alloc "^1.1.0" + buffer-alloc "^1.2.0" end-of-stream "^1.0.0" fs-constants "^1.0.0" readable-stream "^2.3.0" - to-buffer "^1.1.0" + to-buffer "^1.1.1" xtend "^4.0.0" tar@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= dependencies: block-stream "*" fstream "^1.0.2" @@ -1684,6 +1989,7 @@ tar@^2.0.0: tiny-lr@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-0.2.1.tgz#b3fdba802e5d56a33c2f6f10794b32e477ac729d" + integrity sha1-s/26gC5dVqM8L28QeUsy5Hescp0= dependencies: body-parser "~1.14.0" debug "~2.2.0" @@ -1692,44 +1998,53 @@ tiny-lr@^0.2.1: parseurl "~1.3.0" qs "~5.1.0" -to-buffer@^1.1.0: +to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: + psl "^1.1.24" punycode "^1.4.1" trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-is@~1.6.10: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== dependencies: media-typer "0.3.0" mime-types "~2.1.18" uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== underscore.string@~3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" + version "3.3.5" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" + integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== dependencies: sprintf-js "^1.0.3" util-deprecate "^1.0.2" @@ -1737,22 +2052,34 @@ underscore.string@~3.3.4: underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uuid@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" +uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== validate-npm-package-license@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" @@ -1760,6 +2087,7 @@ validate-npm-package-license@^3.0.1: verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -1768,10 +2096,12 @@ verror@1.10.0: walkdir@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" + integrity sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI= websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + integrity sha1-DK+dLXVdk67gSdS90NP+LMoqJOs= dependencies: http-parser-js ">=0.4.0" websocket-extensions ">=0.1.1" @@ -1779,34 +2109,41 @@ websocket-driver@>=0.5.1: websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== which-pm-runs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= which@1, which@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= zip-stream@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04" + integrity sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ= dependencies: archiver-utils "^1.3.0" compress-commons "^1.2.0"