From dd034743a362402860eac0a2ed5f3e2beb435725 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 20 Jan 2019 11:39:06 +0100 Subject: [PATCH 01/73] build(grunt): Don't include unused rinvex/countries files in release --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6850a24..719f870 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,7 +57,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: { From 93878220b52c1209da08178e1513199d722ae89c Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Fri, 25 Jan 2019 21:16:58 +0100 Subject: [PATCH 02/73] fix(playlist): Unset title variable --- templates/playlist.tpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/templates/playlist.tpl b/templates/playlist.tpl index 39617a0..84134b1 100644 --- a/templates/playlist.tpl +++ b/templates/playlist.tpl @@ -17,8 +17,12 @@ {/if} {$video->url} {/strip}"> - {if !isset($video->title) and $video->ie_key == YoutubePlaylist} - Playlist + {if !isset($video->title)} + {if $video->ie_key == YoutubePlaylist} + Playlist + {else} + Video + {/if} {else} {$video->title} {/if} From 5594af95c1a1a35f08b2f3358596ea6759bd2801 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Thu, 14 Feb 2019 16:16:56 +0100 Subject: [PATCH 03/73] build(composer): Dependencies update symfony/yaml, symfony/process, symfony/var-dumper, phpunit/phpunit, heroku/heroku-buildpack-php, mockery/mockery --- composer.lock | 70 ++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/composer.lock b/composer.lock index 2ab3bf0..d107cf8 100644 --- a/composer.lock +++ b/composer.lock @@ -422,16 +422,16 @@ }, { "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", @@ -1242,16 +1242,16 @@ }, { "name": "symfony/process", - "version": "v3.4.21", + "version": "v3.4.22", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c" + "reference": "009f8dda80930e89e8344a4e310b08f9ff07dd2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", + "url": "https://api.github.com/repos/symfony/process/zipball/009f8dda80930e89e8344a4e310b08f9ff07dd2e", + "reference": "009f8dda80930e89e8344a4e310b08f9ff07dd2e", "shasum": "" }, "require": { @@ -1287,20 +1287,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-01-02T21:24:08+00:00" + "time": "2019-01-16T13:27:11+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.21", + "version": "v3.4.22", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea" + "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/554a59a1ccbaac238a89b19c8e551a556fd0e2ea", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ba11776e9e6c15ad5759a07bffb15899bac75c2d", + "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d", "shasum": "" }, "require": { @@ -1346,7 +1346,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2019-01-16T10:59:17+00:00" }, { "name": "zonuexe/http-accept-language", @@ -1492,9 +1492,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 +1501,16 @@ }, { "name": "heroku/heroku-buildpack-php", - "version": "v148", + "version": "v150", "source": { "type": "git", "url": "https://github.com/heroku/heroku-buildpack-php.git", - "reference": "d331bfb9251d8a091d3a0d29e25ffcdb801577e1" + "reference": "e19cd3062208c1be2ad90130744a048abadc434b" }, "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/e19cd3062208c1be2ad90130744a048abadc434b", + "reference": "e19cd3062208c1be2ad90130744a048abadc434b", "shasum": "" }, "bin": [ @@ -1543,7 +1541,7 @@ "nginx", "php" ], - "time": "2018-12-20T21:53:40+00:00" + "time": "2019-02-07T19:46:31+00:00" }, { "name": "myclabs/deep-copy", @@ -2117,16 +2115,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 +2195,7 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" + "time": "2019-02-01T05:22:47+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -2263,9 +2261,7 @@ "version": "2019.01.17", "dist": { "type": "zip", - "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip", - "reference": null, - "shasum": null + "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip" }, "type": "library" }, @@ -2940,16 +2936,16 @@ }, { "name": "symfony/var-dumper", - "version": "v3.4.21", + "version": "v3.4.22", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a5f39641bb62e8b74e343467b145331273f615a2" + "reference": "2159335b452d929cbb9921fc4eb7d1bfed32d0be" }, "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/2159335b452d929cbb9921fc4eb7d1bfed32d0be", + "reference": "2159335b452d929cbb9921fc4eb7d1bfed32d0be", "shasum": "" }, "require": { @@ -3005,7 +3001,7 @@ "debug", "dump" ], - "time": "2019-01-01T13:45:19+00:00" + "time": "2019-01-29T16:19:17+00:00" }, { "name": "theseer/tokenizer", From 2ebe1a5bb0bfb32c5668170a1026a37f2c421a64 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 24 Mar 2019 14:26:11 +0100 Subject: [PATCH 04/73] build(composer): Upgrade rg3/youtube-dl to 2019.03.18 To fix an issue with Vimeo videos --- composer.json | 6 ++-- composer.lock | 95 ++++++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/composer.json b/composer.json index 5ef3ef0..ae53e60 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "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.03.18", "heroku/heroku-buildpack-php": "*", "anam/phantomjs-linux-x86-binary": "~2.1.1" }, @@ -39,10 +39,10 @@ "type": "package", "package": { "name": "rg3/youtube-dl", - "version": "2019.01.17", + "version": "2019.03.18", "dist": { "type": "zip", - "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip" + "url": "https://github.com/rg3/youtube-dl/archive/2019.03.18.zip" } } }, diff --git a/composer.lock b/composer.lock index d107cf8..2d9aab1 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": "4100301b2d759b17125edf560f063cc5", "packages": [ { "name": "aura/session", @@ -533,27 +533,27 @@ }, { "name": "php-mock/php-mock", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock.git", - "reference": "22d297231118e6fd5b9db087fbe1ef866c2b95d2" + "reference": "611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb" }, "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/611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb", + "reference": "611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb", "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-03-04T21:09:32+00:00" }, { "name": "php-mock/php-mock-integration", @@ -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,11 +1241,11 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/process", - "version": "v3.4.22", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -1291,16 +1294,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.22", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d" + "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/ba11776e9e6c15ad5759a07bffb15899bac75c2d", - "reference": "ba11776e9e6c15ad5759a07bffb15899bac75c2d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/57f1ce82c997f5a8701b89ef970e36bb657fd09c", + "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c", "shasum": "" }, "require": { @@ -1346,7 +1349,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-01-16T10:59:17+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "zonuexe/http-accept-language", @@ -1501,16 +1504,16 @@ }, { "name": "heroku/heroku-buildpack-php", - "version": "v150", + "version": "v153", "source": { "type": "git", "url": "https://github.com/heroku/heroku-buildpack-php.git", - "reference": "e19cd3062208c1be2ad90130744a048abadc434b" + "reference": "59b9c4daeebd70eadc4ae3d394429e3f357c56e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/e19cd3062208c1be2ad90130744a048abadc434b", - "reference": "e19cd3062208c1be2ad90130744a048abadc434b", + "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/59b9c4daeebd70eadc4ae3d394429e3f357c56e8", + "reference": "59b9c4daeebd70eadc4ae3d394429e3f357c56e8", "shasum": "" }, "bin": [ @@ -1541,7 +1544,7 @@ "nginx", "php" ], - "time": "2019-02-07T19:46:31+00:00" + "time": "2019-03-19T00:24:16+00:00" }, { "name": "myclabs/deep-copy", @@ -2258,10 +2261,10 @@ }, { "name": "rg3/youtube-dl", - "version": "2019.01.17", + "version": "2019.03.18", "dist": { "type": "zip", - "url": "https://github.com/rg3/youtube-dl/archive/2019.01.17.zip" + "url": "https://github.com/rg3/youtube-dl/archive/2019.03.18.zip" }, "type": "library" }, @@ -2826,16 +2829,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.4.0", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "379deb987e26c7cd103a7b387aea178baec96e48" + "reference": "5b4333b4010625d29580eb4a41f1e53251be6baa" }, "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/5b4333b4010625d29580eb4a41f1e53251be6baa", + "reference": "5b4333b4010625d29580eb4a41f1e53251be6baa", "shasum": "" }, "require": { @@ -2868,25 +2871,25 @@ } ], "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-03-19T03:22:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", "shasum": "" }, "require": { @@ -2898,7 +2901,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -2932,20 +2935,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.22", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2159335b452d929cbb9921fc4eb7d1bfed32d0be" + "reference": "d34d10236300876d14291e9df85c6ef3d3bb9066" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2159335b452d929cbb9921fc4eb7d1bfed32d0be", - "reference": "2159335b452d929cbb9921fc4eb7d1bfed32d0be", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d34d10236300876d14291e9df85c6ef3d3bb9066", + "reference": "d34d10236300876d14291e9df85c6ef3d3bb9066", "shasum": "" }, "require": { @@ -3001,7 +3004,7 @@ "debug", "dump" ], - "time": "2019-01-29T16:19:17+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "theseer/tokenizer", From 506584ceee4571298c77522f689078d6b94860a0 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 24 Mar 2019 15:13:01 +0100 Subject: [PATCH 05/73] fix: Specify allowed protocols explicitely "^=http" also catches http_dash_segments --- controllers/FrontController.php | 13 ++++--------- templates/video.tpl | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 7c27d75..1e22d87 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -67,7 +67,7 @@ class FrontController * * @var string */ - private $defaultFormat = 'best[protocol^=http]'; + private $defaultFormat = 'best[protocol=https]/best[protocol=http]'; /** * LocaleManager instance. @@ -265,14 +265,14 @@ class FrontController private function getAudioResponse(Request $request, Response $response, array $params, $password = null) { try { - if (isset($params['from']) || isset($params['to'])) { + if (isset($params['from']) && !empty($params['from']) || isset($params['to']) && !empty($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); + $urls = $this->download->getURL($params['url'], 'mp3[protocol=https]/mp3[protocol=http]', $password); return $response->withRedirect($urls[0]); } @@ -300,11 +300,7 @@ class FrontController } catch (PasswordException $e) { return $this->password($request, $response); } - if ($this->config->stream) { - $protocol = ''; - } else { - $protocol = '[protocol^=http]'; - } + if (isset($video->entries)) { $template = 'playlist.tpl'; } else { @@ -324,7 +320,6 @@ class FrontController 'class' => 'video', 'title' => $title, 'description' => $description, - 'protocol' => $protocol, 'config' => $this->config, 'canonical' => $this->getCanonicalUrl($request), 'locale' => $this->localeManager->getLocale(), diff --git a/templates/video.tpl b/templates/video.tpl index cdf4242..4dfef1a 100644 --- a/templates/video.tpl +++ b/templates/video.tpl @@ -27,7 +27,7 @@ {/if} - {/if} - From 0ed788560ccfca0f7a0a5eea19f9de8a3ad5cf88 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 30 Mar 2019 18:10:51 +0100 Subject: [PATCH 07/73] refactor: PlaylistArchiveStream should call parent constructor --- classes/PlaylistArchiveStream.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 3c45a04..56d32d2 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -66,6 +66,8 @@ class PlaylistArchiveStream extends TarArchive */ public function __construct(Config $config = null) { + parent::__construct(); + $this->client = new Client(); $this->download = new VideoDownload($config); } From 5835bd67a549d02ff7cb89a7bba3843e61b6175c Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 30 Mar 2019 18:21:45 +0100 Subject: [PATCH 08/73] refactor(phpstan): Various code improvements --- classes/Config.php | 10 ++++------ classes/LocaleManager.php | 4 ++-- classes/LocaleMiddleware.php | 4 ++-- classes/PlaylistArchiveStream.php | 2 +- classes/VideoDownload.php | 20 ++++++++++---------- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/classes/Config.php b/classes/Config.php index cf55404..46b2340 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; @@ -133,11 +133,9 @@ class Config */ public function __construct(array $options) { - if (isset($options) && is_array($options)) { - foreach ($options as $option => $value) { - if (isset($this->$option) && isset($value)) { - $this->$option = $value; - } + foreach ($options as $option => $value) { + if (isset($this->$option) && isset($value)) { + $this->$option = $value; } } $this->getEnv(); diff --git a/classes/LocaleManager.php b/classes/LocaleManager.php index cbc1ddd..af714ba 100644 --- a/classes/LocaleManager.php +++ b/classes/LocaleManager.php @@ -24,7 +24,7 @@ class LocaleManager /** * Current locale. * - * @var Locale + * @var Locale|null */ private $curLocale; @@ -78,7 +78,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 index 56d32d2..8ee5d14 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -186,7 +186,7 @@ class PlaylistArchiveStream extends TarArchive public function stream_read($count) { if (!$this->files[$this->curFile]['headersSent']) { - $urls = $this->download->getUrl($this->files[$this->curFile]['url'], $this->format); + $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'); diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 0f3c462..539462a 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -130,7 +130,7 @@ class VideoDownload * @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) { @@ -214,7 +214,7 @@ class VideoDownload /** * Return arguments used to run rtmp for a specific video. * - * @param object $video Video object returned by youtube-dl + * @param stdClass $video Video object returned by youtube-dl * * @return array Arguments */ @@ -266,12 +266,12 @@ 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 - * @param string $from Start the conversion at this time - * @param string $to End the conversion at this time + * @param stdClass $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 + * @param string $from Start the conversion at this time + * @param string $to End the conversion at this time * * @throws Exception If avconv/ffmpeg is missing * @@ -484,8 +484,8 @@ class VideoDownload /** * 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 + * @param stdClass $video Video object returned by youtube-dl + * @param string $format Requested format * * @throws Exception If the popen stream was not created correctly * From 80b44af772b1d40d179f0578a4f6ff7d0235ca71 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 30 Mar 2019 18:33:05 +0100 Subject: [PATCH 09/73] style: Clarify long conditions --- controllers/FrontController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 04e6b7a..c2cd9d3 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -263,7 +263,9 @@ class FrontController private function getAudioResponse(Request $request, Response $response, array $params, $password = null) { try { - if (isset($params['from']) && !empty($params['from']) || isset($params['to']) && !empty($params['to'])) { + if ((isset($params['from']) && !empty($params['from'])) + || (isset($params['to']) && !empty($params['to'])) + ) { throw new Exception('Force convert when we need to seek.'); } From ac3b768b508aab55ea8f00cf9ab3f2900149ab5a Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 30 Mar 2019 19:13:48 +0100 Subject: [PATCH 10/73] refactor(phpstan): Various code improvements --- classes/VideoDownload.php | 10 +++++----- controllers/FrontController.php | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 539462a..56e0684 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -89,12 +89,12 @@ class VideoDownload * * @return string */ - private function getProp($url, $format = null, $prop = 'dump-json', $password = null) + private function getProp($url = null, $format = null, $prop = 'dump-json', $password = null) { - $arguments = [ - '--'.$prop, - $url, - ]; + $arguments = ['--'.$prop]; + if (isset($url)) { + $arguments[] = $url; + } if (isset($format)) { $arguments[] = '-f '.$format; } diff --git a/controllers/FrontController.php b/controllers/FrontController.php index c2cd9d3..4e73858 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -79,9 +79,9 @@ class FrontController /** * FrontController constructor. * - * @param Container $container Slim dependency container - * @param Config $config Config instance - * @param array $cookies Cookie array + * @param ContainerInterface $container Slim dependency container + * @param Config $config Config instance + * @param array $cookies Cookie array */ public function __construct(ContainerInterface $container, Config $config = null, array $cookies = []) { @@ -112,7 +112,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', From 06f665bdad3f085a046c1b95142c233202b45f6b Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 31 Mar 2019 17:18:57 +0200 Subject: [PATCH 11/73] build(yarn): Add grunt-phpstan --- Gruntfile.js | 15 +- composer.json | 3 +- composer.lock | 963 +++++++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + yarn.lock | 5 + 5 files changed, 984 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 719f870..6a18b77 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -33,6 +33,18 @@ module.exports = function (grunt) { src: ['tests/*.php'] } }, + phpstan: { + options: { + level: 'max', + bin: 'vendor/bin/phpstan' + }, + php: { + src: ['*.php', 'classes/*.php', 'controllers/*.php'] + }, + tests: { + src: ['tests/*.php'] + } + }, jslint: { js: { src: ['js/*.js'] @@ -125,9 +137,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/composer.json b/composer.json index ae53e60..c704f41 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "ffmpeg/ffmpeg": "4.0.3", "rg3/youtube-dl": "2019.03.18", "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": { diff --git a/composer.lock b/composer.lock index 2d9aab1..d1f4c06 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": "4100301b2d759b17125edf560f063cc5", + "content-hash": "738c99fb7051e900838359d31c463e7e", "packages": [ { "name": "aura/session", @@ -1546,6 +1546,57 @@ ], "time": "2019-03-19T00:24:16+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", "version": "1.7.0", @@ -1591,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", @@ -1908,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", @@ -2257,8 +2993,56 @@ "mock", "xunit" ], + "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, + { + "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://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.03.18", @@ -2878,6 +3662,183 @@ ], "time": "2019-03-19T03:22:27+00:00" }, + { + "name": "symfony/console", + "version": "v3.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "71ce77f37af0c5ffb9590e43cc4f70e426945c5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/71ce77f37af0c5ffb9590e43cc4f70e426945c5e", + "reference": "71ce77f37af0c5ffb9590e43cc4f70e426945c5e", + "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-02-23T15:06:07+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "8d8a9e877b3fcdc50ddecf8dcea146059753f782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/8d8a9e877b3fcdc50ddecf8dcea146059753f782", + "reference": "8d8a9e877b3fcdc50ddecf8dcea146059753f782", + "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-02-24T15:45:11+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/fcdde4aa38f48190ce70d782c166f23930084f9b", + "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b", + "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-02-22T14:44:53+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.11.0", diff --git a/package.json b/package.json index 804e447..8c94ab5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "grunt-markdownlint": "~2.1.0", "grunt-phpcs": "~0.4.0", "grunt-phpdocumentor": "~0.4.1", + "grunt-phpstan": "~0.1.0", "grunt-phpunit": "~0.3.6" }, "homepage": "https://www.alltubedownload.net/", diff --git a/yarn.lock b/yarn.lock index e0e5bad..ebed323 100644 --- a/yarn.lock +++ b/yarn.lock @@ -755,6 +755,11 @@ grunt-phpdocumentor@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/grunt-phpdocumentor/-/grunt-phpdocumentor-0.4.1.tgz#513a5cefd8804d45792ea35ddfbcb2471e307141" +grunt-phpstan@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/grunt-phpstan/-/grunt-phpstan-0.1.0.tgz#b6009f9d54884d2427202e91ebc080abc5c3fc10" + integrity sha512-SL8h4cyWNoAf043Uao1ZZWVNgbJJN94Ogey1dDA8SGXeuFiGygYES4uDoqO59SYLmmnkB/zAiTznCA99QWL7Wg== + grunt-phpunit@~0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/grunt-phpunit/-/grunt-phpunit-0.3.6.tgz#0e75bee6b5c2e65fda45075672a06ceb2cecd869" From 7583eb1f5f4d90a249ca6fe15319a4c1f66691f8 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 31 Mar 2019 17:20:20 +0200 Subject: [PATCH 12/73] build(composer): Dependencies update mathmarques/smarty-view --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index d1f4c06..8ae218c 100644 --- a/composer.lock +++ b/composer.lock @@ -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,7 +418,7 @@ "template", "view" ], - "time": "2016-08-25T19:04:49+00:00" + "time": "2019-03-31T14:42:41+00:00" }, { "name": "mockery/mockery", From 8229417fc2b9725bdef938f20057d477cb421a29 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Wed, 10 Apr 2019 21:44:57 +0200 Subject: [PATCH 13/73] build(composer): Dependencies update --- composer.json | 2 +- composer.lock | 68 +++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/composer.json b/composer.json index c704f41..575ff65 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "rg3/youtube-dl": "2019.03.18", "heroku/heroku-buildpack-php": "*", "anam/phantomjs-linux-x86-binary": "~2.1.1", - "phpstan/phpstan": "^0.9.2" + "phpstan/phpstan": "~0.9.2" }, "extra": { "paas": { diff --git a/composer.lock b/composer.lock index 8ae218c..b48ba78 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": "738c99fb7051e900838359d31c463e7e", + "content-hash": "56a4c280b6d56ee4689731edc5e5f19a", "packages": [ { "name": "aura/session", @@ -533,16 +533,16 @@ }, { "name": "php-mock/php-mock", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/php-mock/php-mock.git", - "reference": "611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb" + "reference": "e2eea560cb01502148ca895221f0b58806c5a4df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mock/php-mock/zipball/611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb", - "reference": "611ccd15f53d70b4d1fd31e886f7a5c08dc53bbb", + "url": "https://api.github.com/repos/php-mock/php-mock/zipball/e2eea560cb01502148ca895221f0b58806c5a4df", + "reference": "e2eea560cb01502148ca895221f0b58806c5a4df", "shasum": "" }, "require": { @@ -593,7 +593,7 @@ "test", "test double" ], - "time": "2019-03-04T21:09:32+00:00" + "time": "2019-04-05T22:15:19+00:00" }, { "name": "php-mock/php-mock-integration", @@ -1245,7 +1245,7 @@ }, { "name": "symfony/process", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -1294,16 +1294,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c" + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/57f1ce82c997f5a8701b89ef970e36bb657fd09c", - "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c", + "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", "shasum": "" }, "require": { @@ -1349,7 +1349,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-03-25T07:48:46+00:00" }, { "name": "zonuexe/http-accept-language", @@ -1504,16 +1504,16 @@ }, { "name": "heroku/heroku-buildpack-php", - "version": "v153", + "version": "v154", "source": { "type": "git", "url": "https://github.com/heroku/heroku-buildpack-php.git", - "reference": "59b9c4daeebd70eadc4ae3d394429e3f357c56e8" + "reference": "2625279c4b3caf5e2937308d07d4357b411d04d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/59b9c4daeebd70eadc4ae3d394429e3f357c56e8", - "reference": "59b9c4daeebd70eadc4ae3d394429e3f357c56e8", + "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/2625279c4b3caf5e2937308d07d4357b411d04d0", + "reference": "2625279c4b3caf5e2937308d07d4357b411d04d0", "shasum": "" }, "bin": [ @@ -1544,7 +1544,7 @@ "nginx", "php" ], - "time": "2019-03-19T00:24:16+00:00" + "time": "2019-04-04T22:05:24+00:00" }, { "name": "jean85/pretty-package-versions", @@ -3664,16 +3664,16 @@ }, { "name": "symfony/console", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "71ce77f37af0c5ffb9590e43cc4f70e426945c5e" + "reference": "98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/71ce77f37af0c5ffb9590e43cc4f70e426945c5e", - "reference": "71ce77f37af0c5ffb9590e43cc4f70e426945c5e", + "url": "https://api.github.com/repos/symfony/console/zipball/98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e", + "reference": "98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e", "shasum": "" }, "require": { @@ -3732,20 +3732,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-03-31T11:33:18+00:00" }, { "name": "symfony/debug", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "8d8a9e877b3fcdc50ddecf8dcea146059753f782" + "reference": "adbdd5d66342fb0a0bce7422ba68181842b6610d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/8d8a9e877b3fcdc50ddecf8dcea146059753f782", - "reference": "8d8a9e877b3fcdc50ddecf8dcea146059753f782", + "url": "https://api.github.com/repos/symfony/debug/zipball/adbdd5d66342fb0a0bce7422ba68181842b6610d", + "reference": "adbdd5d66342fb0a0bce7422ba68181842b6610d", "shasum": "" }, "require": { @@ -3788,11 +3788,11 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-02-24T15:45:11+00:00" + "time": "2019-03-10T17:07:42+00:00" }, { "name": "symfony/finder", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -3900,7 +3900,7 @@ }, { "name": "symfony/var-dumper", - "version": "v3.4.23", + "version": "v3.4.24", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", @@ -3969,16 +3969,16 @@ }, { "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": { @@ -4005,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", From 60350747a55bd8353c8bd72d5d890e8cf600bf98 Mon Sep 17 00:00:00 2001 From: Damien Senger Date: Sat, 13 Apr 2019 12:19:11 +0200 Subject: [PATCH 14/73] Adds a label to social sharing links According to the WCAG (success criterias 2.4.4 and 4.1.2), the purpose of each link should be determined from the link text alone or from the link text together with its programmatically determined link context. In this case, because there is no text content, we are using `aria-label` to have an explicit purpose announced to users of assisting technologies. Resources: - https://dequeuniversity.com/rules/axe/3.2/link-name - https://www.w3.org/TR/WCAG21/#link-purpose-in-context - https://www.w3.org/TR/WCAG20-TECHS/ARIA8.html --- templates/inc/header.tpl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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}
From 27543f90240d50717c2403753a2f6ac60c34b4b7 Mon Sep 17 00:00:00 2001 From: Damien Senger Date: Sat, 13 Apr 2019 12:50:57 +0200 Subject: [PATCH 15/73] Updates the doctype to be lowercase --- templates/inc/head.tpl | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) 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} + + + + + + + + + + From 726e9bf5c1a39913428df825e240a4eb384beccb Mon Sep 17 00:00:00 2001 From: Damien Senger Date: Sat, 13 Apr 2019 12:57:42 +0200 Subject: [PATCH 16/73] Adds a label to audio options inputs According to the WCAG success criterias 1.3.1 (Info and relationships), 3.3.2 (Labels or Instructions) and 4.1.2 (Name, Role, Value), labels or instructions are required when content requires user input. To do so, this commit adds a label for the "From" and the "To" inputs displayed when a user chose to get the audio of a file only. Resources: - https://www.w3.org/TR/WCAG21/#labels-or-instructions - https://www.w3.org/TR/WCAG21/#info-and-relationships - https://www.w3.org/TR/WCAG21/#name-role-value - https://www.w3.org/TR/WCAG20-TECHS/H44.html - https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions.html --- templates/index.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/index.tpl b/templates/index.tpl index d151acb..7d0e2de 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -25,8 +25,8 @@ {t}Audio only (MP3){/t}
- {t}From{/t} - {t}to{/t} + +
From ce07a621e503bef744bf9cb403a775ad548706d9 Mon Sep 17 00:00:00 2001 From: Damien Senger Date: Sat, 13 Apr 2019 13:11:09 +0200 Subject: [PATCH 17/73] Improves a11y of password protected video form According to the WCAG success criterias 1.3.1 (Info and relationships), 3.3.2 (Labels or Instructions) and 4.1.2 (Name, Role, Value), labels or instructions are required when content requires user input. To do so, this commit adds a label for the password input and hide it with a CSS visually-hidden method to not alter the design. Resources: - https://www.w3.org/TR/WCAG21/#labels-or-instructions - https://www.w3.org/TR/WCAG21/#info-and-relationships - https://www.w3.org/TR/WCAG21/#name-role-value - https://www.w3.org/TR/WCAG20-TECHS/H44.html - https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions.html - https://gomakethings.com/hidden-content-for-better-a11y/ --- css/style.css | 894 +++++++++++++++++++++-------------------- templates/password.tpl | 3 +- 2 files changed, 450 insertions(+), 447 deletions(-) diff --git a/css/style.css b/css/style.css index 951ef4d..a5e7a6c 100644 --- a/css/style.css +++ b/css/style.css @@ -1,733 +1,735 @@ - 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; +} + +.sr-only.focusable:active, +.sr-only.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + white-space: normal; + width: auto; } 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}

- + +

From 596d5e29942c7f63b2e41b10eed21456d778a12c Mon Sep 17 00:00:00 2001 From: Damien Senger Date: Sat, 13 Apr 2019 13:17:13 +0200 Subject: [PATCH 18/73] Adds and fixes labels for video.tpl inputs According to the WCAG success criterias 1.3.1 (Info and relationships), 3.3.2 (Labels or Instructions) and 4.1.2 (Name, Role, Value), labels or instructions are required when content requires user input. The bit rate label was replaced by a real label announcing the purpose of the text input _Custom bitrate_. The existing unit indication is now using a `` attribute and it is linked to the input using an `aria-describedby` attribute. Also, the lack of label for the format conversion selectbox is fixed by using a specific `aria-label` attribute. Resources: - https://www.w3.org/TR/WCAG21/#labels-or-instructions - https://www.w3.org/TR/WCAG21/#info-and-relationships - https://www.w3.org/TR/WCAG21/#name-role-value - https://www.w3.org/TR/WCAG20-TECHS/H44.html - https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions.html - https://www.w3.org/TR/WCAG20-TECHS/ARIA1.html --- templates/video.tpl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/templates/video.tpl b/templates/video.tpl index cdf4242..d7b1f83 100644 --- a/templates/video.tpl +++ b/templates/video.tpl @@ -83,14 +83,15 @@ {if $config->convertAdvanced} - {foreach $config->convertAdvancedFormats as $format} {/foreach} {t}with{/t} - - + + + {t}kbit/s audio{/t}

{/if}
From a5fbb740940b8c1ea549cadbadedb3a445932b1c Mon Sep 17 00:00:00 2001 From: hiwelo Date: Thu, 18 Apr 2019 23:04:22 +0200 Subject: [PATCH 19/73] Fixes attributes used to link label & form inputs --- templates/index.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/index.tpl b/templates/index.tpl index 7d0e2de..1f3c252 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -25,8 +25,8 @@ {t}Audio only (MP3){/t}
- - + +
From 71200c80bc561736aaaa4277fa861ee9a93578d0 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Fri, 19 Apr 2019 23:26:03 +0200 Subject: [PATCH 20/73] test(phpunit): Disable Vimeo tests on CI --- tests/FrontControllerTest.php | 12 ++++++++++++ tests/VideoDownloadTest.php | 29 ++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 42b9d19..ee4490c 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -292,6 +292,9 @@ class FrontControllerTest extends TestCase */ public function testVideoWithVimeoAudio() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } // So we can test the fallback to default format $this->assertRequestIsOk('video', ['url' => 'https://vimeo.com/251997032', 'audio' => true]); } @@ -319,6 +322,9 @@ class FrontControllerTest extends TestCase */ public function testVideoWithPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $result = $this->controller->video( $this->request->withQueryParams(['url' => 'http://vimeo.com/68375962']) ->withParsedBody(['password' => 'youtube-dl']), @@ -334,6 +340,9 @@ class FrontControllerTest extends TestCase */ public function testVideoWithMissingPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $this->assertRequestIsOk('video', ['url' => 'http://vimeo.com/68375962']); $this->assertRequestIsOk('video', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); } @@ -502,6 +511,9 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithMissingPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $this->assertRequestIsRedirect('redirect', ['url' => 'http://vimeo.com/68375962']); } diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index f93a249..0614d95 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -117,6 +117,9 @@ class VideoDownloadTest extends TestCase */ public function testGetURLWithPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $videoURL = $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl'); $this->assertContains('vimeocdn.com', $videoURL[0]); } @@ -129,6 +132,9 @@ class VideoDownloadTest extends TestCase */ public function testGetURLWithMissingPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $this->download->getURL('http://vimeo.com/68375962'); } @@ -140,6 +146,9 @@ class VideoDownloadTest extends TestCase */ public function testGetURLWithWrongPassword() { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } $this->download->getURL('http://vimeo.com/68375962', null, 'foo'); } @@ -164,7 +173,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 +187,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 +206,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; } /** From f5408d409a166d60ca851e5b18be9b192d2be7d2 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Fri, 19 Apr 2019 23:28:02 +0200 Subject: [PATCH 21/73] style(csslint): Remove unused CSS class --- css/style.css | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/css/style.css b/css/style.css index a5e7a6c..482ff25 100644 --- a/css/style.css +++ b/css/style.css @@ -722,14 +722,3 @@ h1 { white-space: nowrap; width: 1px; } - -.sr-only.focusable:active, -.sr-only.focusable:focus { - clip: auto; - height: auto; - margin: 0; - overflow: visible; - position: static; - white-space: normal; - width: auto; -} From cd623bfa361f92701eb8469f1709d5365b411324 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Fri, 19 Apr 2019 23:30:44 +0200 Subject: [PATCH 22/73] ci(appveyor): Fix path to PHP config --- .appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 817effc..dfc50f4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,12 +4,12 @@ install: - net start wuauserv - cinst php 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\php73\php.ini-development C:\tools\php73\php.ini + - echo extension=C:\tools\php73\ext\php_gmp.dll >> C:\tools\php73\php.ini + - echo extension=C:\tools\php73\ext\php_gettext.dll >> C:\tools\php73\php.ini + - echo extension=C:\tools\php73\ext\php_intl.dll >> C:\tools\php73\php.ini + - echo extension=C:\tools\php73\ext\php_openssl.dll >> C:\tools\php73\php.ini + - echo extension=C:\tools\php73\ext\php_mbstring.dll >> C:\tools\php73\php.ini - composer install --no-dev - composer global require phpunit/phpunit - C:\Python36\python.exe -m pip install youtube-dl From 4537b661e12676de6b74044eaddc3909113b6ae0 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 00:37:49 +0200 Subject: [PATCH 23/73] fix: Close playlist streams correctly --- classes/PlaylistArchiveStream.php | 12 ++++++++++++ tests/PlaylistArchiveStreamTest.php | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 8ee5d14..b050c61 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -208,4 +208,16 @@ class PlaylistArchiveStream extends TarArchive return fread($this->buffer, $count); } + + /** + * Called when fclose() is used on the stream. + * + * @return void + */ + public function stream_close() + { + if (is_resource($this->buffer)) { + fclose($this->buffer); + } + } } diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index fde76c6..36a49d3 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -34,6 +34,16 @@ class PlaylistArchiveStreamTest extends TestCase $this->stream = new PlaylistArchiveStream(Config::getInstance('config/'.$configFile)); } + /** + * Clean variables used in tests. + * + * @return void + */ + protected function tearDown() + { + $this->stream->stream_close(); + } + /** * Test the stream_open() function. * From c9cc1907aca163c4f1af23656b08a40a866d8faf Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 01:00:04 +0200 Subject: [PATCH 24/73] ci(appveyor): Force phpunit 6 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index dfc50f4..3a0cb53 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -11,7 +11,7 @@ install: - echo extension=C:\tools\php73\ext\php_openssl.dll >> C:\tools\php73\php.ini - echo extension=C:\tools\php73\ext\php_mbstring.dll >> C:\tools\php73\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: From 7ab4c555500eb76d9fc4e959d4dcb19a54bc4421 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 01:19:55 +0200 Subject: [PATCH 25/73] docs: Add comments to some tricky parts --- classes/PlaylistArchiveStream.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index b050c61..98cba0c 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -82,8 +82,11 @@ class PlaylistArchiveStream extends TarArchive protected function send($data) { $pos = ftell($this->buffer); + + // Add data to the buffer. fwrite($this->buffer, $data); if ($pos !== false) { + // Rewind so that stream_read() can later read this data. fseek($this->buffer, $pos); } } From 07261db27bff2c2ed9e04761b2c88aa5bc6a0a97 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 11:27:13 +0200 Subject: [PATCH 26/73] fix: Don't call PlaylistArchiveStream parent constructor Because it messes with the output buffer Fixes #215 --- Gruntfile.js | 3 +- classes/PlaylistArchiveStream.php | 2 - package.json | 2 +- phpstan.neon | 4 + yarn.lock | 728 ++++++++++++++++++++++-------- 5 files changed, 537 insertions(+), 202 deletions(-) create mode 100644 phpstan.neon diff --git a/Gruntfile.js b/Gruntfile.js index 6a18b77..86d32e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -36,7 +36,8 @@ module.exports = function (grunt) { phpstan: { options: { level: 'max', - bin: 'vendor/bin/phpstan' + bin: 'vendor/bin/phpstan', + config: 'phpstan.neon' }, php: { src: ['*.php', 'classes/*.php', 'controllers/*.php'] diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 98cba0c..08ee875 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -66,8 +66,6 @@ class PlaylistArchiveStream extends TarArchive */ public function __construct(Config $config = null) { - parent::__construct(); - $this->client = new Client(); $this->download = new VideoDownload($config); } diff --git a/package.json b/package.json index 8c94ab5..5bb82aa 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "grunt-markdownlint": "~2.1.0", "grunt-phpcs": "~0.4.0", "grunt-phpdocumentor": "~0.4.1", - "grunt-phpstan": "~0.1.0", + "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..e7225f9 --- /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\\TarArchive\.#' diff --git a/yarn.lock b/yarn.lock index ebed323..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,35 +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.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/grunt-phpstan/-/grunt-phpstan-0.1.0.tgz#b6009f9d54884d2427202e91ebc080abc5c3fc10" - integrity sha512-SL8h4cyWNoAf043Uao1ZZWVNgbJJN94Ogey1dDA8SGXeuFiGygYES4uDoqO59SYLmmnkB/zAiTznCA99QWL7Wg== +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" @@ -785,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" @@ -795,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" @@ -857,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" @@ -876,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" @@ -889,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" @@ -986,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" @@ -995,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" @@ -1018,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" @@ -1032,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" @@ -1046,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" @@ -1061,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" @@ -1081,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" @@ -1154,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" @@ -1161,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" @@ -1195,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" @@ -1238,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" @@ -1274,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" @@ -1312,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" @@ -1337,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" @@ -1344,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" @@ -1368,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" @@ -1377,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" @@ -1386,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" @@ -1393,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" @@ -1401,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" @@ -1413,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" @@ -1425,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" @@ -1438,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" @@ -1517,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" @@ -1533,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" @@ -1597,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" @@ -1604,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" @@ -1667,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" @@ -1689,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" @@ -1697,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" @@ -1742,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" @@ -1765,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" @@ -1773,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" @@ -1784,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" From 12747ade59297fa06237e7f0f8adcfe76e7ee071 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 11:30:16 +0200 Subject: [PATCH 27/73] ci(travis): Disable Twitter test on CI --- tests/VideoDownloadTest.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index 0614d95..87c0ce9 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -244,14 +244,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; } /** From 0283ef69280272974862570038645168bf48b0c4 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 11:51:07 +0200 Subject: [PATCH 28/73] ci(appveyor): Install a specific PHP version --- .appveyor.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3a0cb53..75001db 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,14 +2,15 @@ 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\php73\php.ini-development C:\tools\php73\php.ini - - echo extension=C:\tools\php73\ext\php_gmp.dll >> C:\tools\php73\php.ini - - echo extension=C:\tools\php73\ext\php_gettext.dll >> C:\tools\php73\php.ini - - echo extension=C:\tools\php73\ext\php_intl.dll >> C:\tools\php73\php.ini - - echo extension=C:\tools\php73\ext\php_openssl.dll >> C:\tools\php73\php.ini - - echo extension=C:\tools\php73\ext\php_mbstring.dll >> C:\tools\php73\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:^6.0 - C:\Python36\python.exe -m pip install youtube-dl From f4654c7229289ec70294ccbb8ad14f32eb1704d7 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 12:17:52 +0200 Subject: [PATCH 29/73] test(phpunit): Improve locale-related tests --- tests/LocaleManagerTest.php | 9 ++++----- tests/LocaleTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/LocaleManagerTest.php b/tests/LocaleManagerTest.php index 2fff818..718f5b2 100644 --- a/tests/LocaleManagerTest.php +++ b/tests/LocaleManagerTest.php @@ -26,19 +26,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/LocaleTest.php b/tests/LocaleTest.php index 3651b22..3cf257b 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -77,4 +77,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()); + } } From ff95b93c209b14a1b5825a6a0af776e779afe0fe Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sat, 20 Apr 2019 12:23:18 +0200 Subject: [PATCH 30/73] fixup! ci(travis): Disable Twitter test on CI --- tests/FrontControllerTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index ee4490c..a7a9c21 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -442,6 +442,9 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithM3uStream() { + if (getenv('CI')) { + $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.'); + } $this->config->stream = true; $this->assertRequestIsOk( 'redirect', From f9bf3b8d47ead2f4a862b37938bbd60af0e06e50 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 00:34:12 +0200 Subject: [PATCH 31/73] refactor: Use a StreamInterface for PlaylistArchiveStream It is much cleaner --- classes/PlaylistArchiveStream.php | 181 ++++++++++++++++++++-------- classes/VideoDownload.php | 24 ---- controllers/FrontController.php | 5 +- index.php | 2 - tests/LocaleManagerTest.php | 2 +- tests/LocaleTest.php | 2 +- tests/PlaylistArchiveStreamTest.php | 178 +++++++++++++++++++-------- tests/VideoDownloadStubsTest.php | 15 --- tests/VideoDownloadTest.php | 15 --- 9 files changed, 267 insertions(+), 157 deletions(-) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 08ee875..70dd68f 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -1,28 +1,29 @@ client = new Client(); $this->download = new VideoDownload($config); + + $this->format = $format; + $buffer = fopen('php://temp', 'r+'); + if ($buffer !== false) { + $this->buffer = $buffer; + } + foreach ($video->entries as $entry) { + $this->files[] = [ + 'url' => $entry->url, + 'headersSent' => false, + 'complete' => false, + 'stream' => null, + ]; + } } /** @@ -84,89 +101,152 @@ class PlaylistArchiveStream extends TarArchive // Add data to the buffer. fwrite($this->buffer, $data); if ($pos !== false) { - // Rewind so that stream_read() can later read this data. + // Rewind so that read() can later read this data. fseek($this->buffer, $pos); } } /** - * Called when fopen() is used on the stream. + * Write data to the stream. * - * @param string $path Playlist path (should be playlist://url1;url2;.../format) + * @param string $string The string that is to be written. * - * @return bool + * @return int */ - public function stream_open($path) + public function write($string) { - $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, - ]; - } + throw new RuntimeException('This stream is not writeable.'); + } + /** + * Get the size of the stream if known. + * + * @return null + */ + public function getSize() + { + return null; + } + + /** + * Returns whether or not the stream is seekable. + * + * @return boolean + */ + public function isSeekable() + { + return false; + } + + /** + * Seek to the beginning of the stream. + * + * @return void + */ + public function rewind() + { + throw new RuntimeException('This stream is not seekable.'); + } + + /** + * Returns whether or not the stream is writable. + * + * @return boolean + */ + public function isWritable() + { + return false; + } + + /** + * Returns whether or not the stream is readable. + * + * @return boolean + */ + public function isReadable() + { return true; } /** - * Called when fwrite() is used on the stream. + * Returns the remaining contents in a string. * - * @return int + * @return string */ - public function stream_write() + public function getContents() { - //We don't support writing to a stream - return 0; + return stream_get_contents($this->buffer); } /** - * Called when fstat() is used on the stream. + * Get stream metadata as an associative array or retrieve a specific key. * - * @return array + * @param string $key string $key Specific metadata to retrieve. + * + * @return null */ - public function stream_stat() + public function getMetadata($key = null) { - //We need this so Slim won't try to get the size of the stream - return [ - 'mode' => 0010000, - ]; + return null; } /** - * Called when ftell() is used on the stream. + * 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() + { + $string = ''; + + foreach ($this->files as $file) { + $string .= $file['url']; + } + + return $string; + } + + /** + * Returns the current position of the file read/write pointer * * @return int|false */ - public function stream_tell() + public function tell() { return ftell($this->buffer); } /** - * Called when fseek() is used on the stream. + * Seek to a position in the stream. * * @param int $offset Offset + * @param int $whence Specifies how the cursor position will be calculated * - * @return bool + * @return void */ - public function stream_seek($offset) + public function seek($offset, $whence = SEEK_SET) { - return fseek($this->buffer, $offset) == 0; + throw new RuntimeException('This stream is not seekable.'); } /** - * Called when feof() is used on the stream. + * Returns true if the stream is at the end of the stream. * * @return bool */ - public function stream_eof() + public function eof() { foreach ($this->files as $file) { if (!$file['complete']) { @@ -178,13 +258,13 @@ class PlaylistArchiveStream extends TarArchive } /** - * Called when fread() is used on the stream. + * Read data from the stream. * * @param int $count Number of bytes to read * * @return string|false */ - public function stream_read($count) + public function read($count) { if (!$this->files[$this->curFile]['headersSent']) { $urls = $this->download->getURL($this->files[$this->curFile]['url'], $this->format); @@ -211,14 +291,19 @@ class PlaylistArchiveStream extends TarArchive } /** - * Called when fclose() is used on the stream. + * Closes the stream and any underlying resources. * * @return void */ - public function stream_close() + public function close() { if (is_resource($this->buffer)) { fclose($this->buffer); } + foreach ($this->files as $file) { + if (is_resource($file['stream'])) { + fclose($file['stream']); + } + } } } diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 56e0684..b32b4d8 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -481,30 +481,6 @@ class VideoDownload return $stream; } - /** - * Get a Tar stream containing every video in the playlist piped through the server. - * - * @param stdClass $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. * diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 4e73858..9789583 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -10,6 +10,7 @@ use Alltube\EmptyUrlException; use Alltube\Locale; use Alltube\LocaleManager; use Alltube\PasswordException; +use Alltube\PlaylistArchiveStream; use Alltube\VideoDownload; use Aura\Session\Segment; use Aura\Session\SessionFactory; @@ -403,14 +404,14 @@ class FrontController { $video = $this->download->getJSON($url, $format, $password); if (isset($video->entries)) { - $stream = $this->download->getPlaylistArchiveStream($video, $format); + $stream = new PlaylistArchiveStream($this->config, $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)); + return $response->withBody($stream); } elseif ($video->protocol == 'rtmp') { $stream = $this->download->getRtmpStream($video); $response = $response->withHeader('Content-Type', 'video/'.$video->ext); diff --git a/index.php b/index.php index a3d4a27..34d8d99 100644 --- a/index.php +++ b/index.php @@ -15,8 +15,6 @@ if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.ph die; } -stream_wrapper_register('playlist', PlaylistArchiveStream::class); - $app = new App(); $container = $app->getContainer(); $config = Config::getInstance(); diff --git a/tests/LocaleManagerTest.php b/tests/LocaleManagerTest.php index 718f5b2..9bf9837 100644 --- a/tests/LocaleManagerTest.php +++ b/tests/LocaleManagerTest.php @@ -10,7 +10,7 @@ use Alltube\LocaleManager; use PHPUnit\Framework\TestCase; /** - * Unit tests for the Config class. + * Unit tests for the LocaleManagerTest class. */ class LocaleManagerTest extends TestCase { diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php index 3cf257b..ba18b84 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -9,7 +9,7 @@ use Alltube\Locale; use PHPUnit\Framework\TestCase; /** - * Unit tests for the Config class. + * Unit tests for the LocaleTest class. */ class LocaleTest extends TestCase { diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index 36a49d3..1c50c54 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -8,6 +8,8 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\PlaylistArchiveStream; use PHPUnit\Framework\TestCase; +use stdClass; +use RuntimeException; /** * Unit tests for the ViewFactory class. @@ -31,7 +33,14 @@ class PlaylistArchiveStreamTest extends TestCase } else { $configFile = 'config_test.yml'; } - $this->stream = new PlaylistArchiveStream(Config::getInstance('config/'.$configFile)); + + $entry = new stdClass(); + $entry->url = 'BaW_jenozKc'; + + $video = new stdClass(); + $video->entries = [$entry, $entry]; + + $this->stream = new PlaylistArchiveStream(Config::getInstance('config/'.$configFile), $video, 'worst'); } /** @@ -41,71 +50,51 @@ class PlaylistArchiveStreamTest extends TestCase */ protected function tearDown() { - $this->stream->stream_close(); + $this->stream->close(); } /** - * Test the stream_open() function. + * Test the write() function. + * + * @return void + * @expectedException RuntimeException + */ + public function testWrite() + { + $this->stream->write('foo'); + } + + + /** + * Test the tell() function. * * @return void */ - public function testStreamOpen() + public function testTell() { - $this->assertTrue($this->stream->stream_open('playlist://foo')); + $this->assertInternalType('int', $this->stream->tell()); } /** - * Test the stream_write() function. + * Test the seek() function. * * @return void + * @expectedException RuntimeException */ - public function testStreamWrite() + public function testSeek() { - $this->assertEquals(0, $this->stream->stream_write()); + $this->stream->seek(42); } /** - * Test the stream_stat() function. + * Test the read() function. * * @return void */ - public function testStreamStat() + public function testRead() { - $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); + while (!$this->stream->eof()) { + $result = $this->stream->read(8192); $this->assertInternalType('string', $result); if (is_string($result)) { $this->assertLessThanOrEqual(8192, strlen($result)); @@ -114,13 +103,104 @@ class PlaylistArchiveStreamTest extends TestCase } /** - * Test the stream_eof() function. + * Test the eof() function. * * @return void */ - public function testStreamEof() + public function testEof() { - $this->stream->stream_open('playlist://foo'); - $this->assertFalse($this->stream->stream_eof()); + $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->assertFalse($this->stream->isSeekable()); + } + + /** + * Test the rewind() function. + * + * @return void + * @expectedException RuntimeException + */ + public function testRewind() + { + $this->stream->rewind(); + } + + /** + * Test the isWritable() function. + * + * @return void + */ + public function testIsWritable() + { + $this->assertFalse($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->assertNull($this->stream->getMetadata()); + } + + /** + * 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/VideoDownloadStubsTest.php b/tests/VideoDownloadStubsTest.php index 71a50ad..c97f5f0 100644 --- a/tests/VideoDownloadStubsTest.php +++ b/tests/VideoDownloadStubsTest.php @@ -110,21 +110,6 @@ class VideoDownloadStubsTest extends TestCase $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. * diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index 87c0ce9..a16c15b 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -533,21 +533,6 @@ class VideoDownloadTest extends TestCase $download->getM3uStream($video); } - /** - * 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')); - } - /** * Test getConvertedStream function without avconv. * From ddc27a8a2c21c4a990b445d7a9507b0fdfe1725b Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 00:56:12 +0200 Subject: [PATCH 32/73] refactor: New PlaylistArchiveVideo class Cleaner way to handle PlaylistArchiveStream info about videos --- classes/PlaylistArchiveStream.php | 70 +++++++++++++++---------------- classes/PlaylistArchiveVideo.php | 49 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 classes/PlaylistArchiveVideo.php diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 70dd68f..a4a75e0 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -19,11 +19,11 @@ use stdClass; class PlaylistArchiveStream extends TarArchive implements StreamInterface { /** - * Files to add in the archive. + * videos to add in the archive. * - * @var array[] + * @var PlaylistArchiveVideo[] */ - private $files = []; + private $videos = []; /** * Stream used to store data before it is sent to the browser. @@ -47,11 +47,11 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface private $download; /** - * Current file position in $files array. + * Current video being streamed to the archive. * * @var int */ - private $curFile = 0; + private $curVideo; /** * Video format to download. @@ -78,12 +78,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface $this->buffer = $buffer; } foreach ($video->entries as $entry) { - $this->files[] = [ - 'url' => $entry->url, - 'headersSent' => false, - 'complete' => false, - 'stream' => null, - ]; + $this->videos[] = new PlaylistArchiveVideo($entry->url); } } @@ -211,8 +206,8 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface { $string = ''; - foreach ($this->files as $file) { - $string .= $file['url']; + foreach ($this->videos as $file) { + $string .= $file->url; } return $string; @@ -248,8 +243,8 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function eof() { - foreach ($this->files as $file) { - if (!$file['complete']) { + foreach ($this->videos as $file) { + if (!$file->complete) { return false; } } @@ -266,25 +261,30 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function 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]); + if (isset($this->curVideo)) { + if (isset($this->curVideo->stream)) { + if (!$this->curVideo->stream->eof()) { + $this->stream_file_part($this->curVideo->stream->read($count)); + } elseif (!$this->curVideo->complete) { + $this->complete_file_stream(); + $this->curVideo->complete = true; + } else { + $this->curVideo = next($this->videos); + } + } else { + $urls = $this->download->getURL($this->curVideo->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] - ); + $contentLengthHeaders = $response->getHeader('Content-Length'); + $this->init_file_stream_transfer( + $this->download->getFilename($this->curVideo->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; + $this->curVideo->stream = $response->getBody(); + } + } else { + $this->curVideo = current($this->videos); } return fread($this->buffer, $count); @@ -300,9 +300,9 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface if (is_resource($this->buffer)) { fclose($this->buffer); } - foreach ($this->files as $file) { - if (is_resource($file['stream'])) { - fclose($file['stream']); + foreach ($this->videos as $file) { + if (is_resource($file->stream)) { + fclose($file->stream); } } } diff --git a/classes/PlaylistArchiveVideo.php b/classes/PlaylistArchiveVideo.php new file mode 100644 index 0000000..cf3609b --- /dev/null +++ b/classes/PlaylistArchiveVideo.php @@ -0,0 +1,49 @@ +url = $url; + } +} From 586f20adb44fb376e1788a41f9871fc0d5182cb5 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 00:58:04 +0200 Subject: [PATCH 33/73] style(styleci): Lint --- classes/PlaylistArchiveStream.php | 15 +++++++-------- classes/PlaylistArchiveVideo.php | 6 ------ index.php | 1 - tests/PlaylistArchiveStreamTest.php | 3 +-- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index a4a75e0..d25a736 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -63,7 +63,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * PlaylistArchiveStream constructor. * - * @param Config $config Config instance. + * @param Config $config Config instance. * @param stdClass $video Video object returned by youtube-dl * @param string $format Requested format */ @@ -120,13 +120,12 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function getSize() { - return null; } /** * Returns whether or not the stream is seekable. * - * @return boolean + * @return bool */ public function isSeekable() { @@ -146,7 +145,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * Returns whether or not the stream is writable. * - * @return boolean + * @return bool */ public function isWritable() { @@ -156,7 +155,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * Returns whether or not the stream is readable. * - * @return boolean + * @return bool */ public function isReadable() { @@ -176,13 +175,12 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * Get stream metadata as an associative array or retrieve a specific key. * - * @param string $key string $key Specific metadata to retrieve. + * @param string $key string $key Specific metadata to retrieve. * * @return null */ public function getMetadata($key = null) { - return null; } /** @@ -194,6 +192,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface { $stream = $this->buffer; $this->close(); + return $stream; } @@ -214,7 +213,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface } /** - * Returns the current position of the file read/write pointer + * Returns the current position of the file read/write pointer. * * @return int|false */ diff --git a/classes/PlaylistArchiveVideo.php b/classes/PlaylistArchiveVideo.php index cf3609b..d67c1ea 100644 --- a/classes/PlaylistArchiveVideo.php +++ b/classes/PlaylistArchiveVideo.php @@ -5,12 +5,6 @@ namespace Alltube; -use Barracuda\ArchiveStream\TarArchive; -use GuzzleHttp\Client; -use Psr\Http\Message\StreamInterface; -use RuntimeException; -use stdClass; - /** * Video streamed to a PlaylistArchiveStream. */ diff --git a/index.php b/index.php index 34d8d99..c42b96e 100644 --- a/index.php +++ b/index.php @@ -5,7 +5,6 @@ use Alltube\Config; use Alltube\Controller\FrontController; use Alltube\LocaleManager; use Alltube\LocaleMiddleware; -use Alltube\PlaylistArchiveStream; use Alltube\UglyRouter; use Alltube\ViewFactory; use Slim\App; diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index 1c50c54..0f07b2e 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -8,8 +8,8 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\PlaylistArchiveStream; use PHPUnit\Framework\TestCase; -use stdClass; use RuntimeException; +use stdClass; /** * Unit tests for the ViewFactory class. @@ -64,7 +64,6 @@ class PlaylistArchiveStreamTest extends TestCase $this->stream->write('foo'); } - /** * Test the tell() function. * From ece51117e6daebd0e1d19778dc731aaf2e1022c2 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 01:04:18 +0200 Subject: [PATCH 34/73] We don't need to do that anymore --- tests/bootstrap.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4f64daf..6a3ee4b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,8 +14,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 */ From 307787251753673bbc431b980e4dc85779cb91b6 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 01:05:19 +0200 Subject: [PATCH 35/73] Typo --- classes/PlaylistArchiveVideo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/PlaylistArchiveVideo.php b/classes/PlaylistArchiveVideo.php index d67c1ea..4610427 100644 --- a/classes/PlaylistArchiveVideo.php +++ b/classes/PlaylistArchiveVideo.php @@ -18,7 +18,7 @@ class PlaylistArchiveVideo public $url; /** - * Has the video been streaded entirely ? + * Has the video been streamed entirely ? * * @var bool */ From feb8998188134d4de939be1641e3c55ee394d8a2 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 09:21:24 +0200 Subject: [PATCH 36/73] fixup! Typo --- tests/bootstrap.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 6a3ee4b..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; /** From 4c9af8ad1d89f89d2ea9390247639793fb4b3d90 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 18:30:02 +0200 Subject: [PATCH 37/73] refactor: New Video class The news class provides a cleaner object-oriented logic BREAKING CHANGE: The VideoDownload class has been removed and the Config constructor is now private --- README.md | 16 +- classes/Config.php | 94 ++++-- classes/PlaylistArchiveStream.php | 151 +++++----- classes/PlaylistArchiveVideo.php | 43 --- classes/{VideoDownload.php => Video.php} | 277 ++++++++++-------- controllers/FrontController.php | 224 ++++++-------- index.php | 6 +- tests/BaseTest.php | 42 +++ tests/ConfigTest.php | 85 ++++-- tests/FrontControllerTest.php | 116 +++----- tests/LocaleManagerTest.php | 2 +- tests/LocaleMiddlewareTest.php | 2 +- tests/LocaleTest.php | 2 +- tests/PlaylistArchiveStreamTest.php | 43 +-- tests/UglyRouterTest.php | 2 +- ...wnloadStubsTest.php => VideoStubsTest.php} | 48 +-- .../{VideoDownloadTest.php => VideoTest.php} | 229 ++++++--------- tests/ViewFactoryTest.php | 2 +- 18 files changed, 665 insertions(+), 719 deletions(-) delete mode 100644 classes/PlaylistArchiveVideo.php rename classes/{VideoDownload.php => Video.php} (64%) create mode 100644 tests/BaseTest.php rename tests/{VideoDownloadStubsTest.php => VideoStubsTest.php} (59%) rename tests/{VideoDownloadTest.php => VideoTest.php} (68%) 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 46b2340..5edf12b 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -129,16 +129,49 @@ 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 = []) + { + $this->applyOptions($options); + $this->getEnv(); + $this->validateOptions(); + } + + /** + * Throw an exception if some of the options are invalid. + * + * @return void + * @throws Exception If youtube-dl is missing + * @throws Exception If Python is missing + */ + 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(); } /** @@ -159,34 +192,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 boolean $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/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index d25a736..a702de1 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -6,7 +6,7 @@ namespace Alltube; use Barracuda\ArchiveStream\TarArchive; -use GuzzleHttp\Client; +use GuzzleHttp\Psr7\Stream; use Psr\Http\Message\StreamInterface; use RuntimeException; use stdClass; @@ -21,7 +21,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * videos to add in the archive. * - * @var PlaylistArchiveVideo[] + * @var Video[] */ private $videos = []; @@ -32,53 +32,32 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ private $buffer; - /** - * Guzzle client. - * - * @var Client - */ - private $client; - - /** - * VideoDownload instance. - * - * @var VideoDownload - */ - private $download; - /** * Current video being streamed to the archive. * - * @var int + * @var Stream */ - private $curVideo; + private $curVideoStream; /** - * Video format to download. - * - * @var string + * True if the archive is complete. + * @var bool */ - private $format; + private $isComplete = false; /** * PlaylistArchiveStream constructor. * - * @param Config $config Config instance. - * @param stdClass $video Video object returned by youtube-dl - * @param string $format Requested format + * @param Video $video Video/playlist to download */ - public function __construct(Config $config, stdClass $video, $format) + public function __construct(Video $video) { - $this->client = new Client(); - $this->download = new VideoDownload($config); - - $this->format = $format; $buffer = fopen('php://temp', 'r+'); if ($buffer !== false) { $this->buffer = $buffer; } foreach ($video->entries as $entry) { - $this->videos[] = new PlaylistArchiveVideo($entry->url); + $this->videos[] = new Video($entry->url); } } @@ -91,26 +70,27 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ protected function send($data) { - $pos = ftell($this->buffer); + $pos = $this->tell(); - // Add data to the buffer. - fwrite($this->buffer, $data); + // 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. - fseek($this->buffer, $pos); + $this->seek($pos); } } /** * Write data to the stream. * - * @param string $string The string that is to be written. + * @param string $string The string that is to be written * * @return int */ public function write($string) { - throw new RuntimeException('This stream is not writeable.'); + fwrite($this->buffer, $string); } /** @@ -129,7 +109,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function isSeekable() { - return false; + return true; } /** @@ -139,7 +119,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function rewind() { - throw new RuntimeException('This stream is not seekable.'); + rewind($this->buffer); } /** @@ -149,7 +129,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function isWritable() { - return false; + return true; } /** @@ -181,6 +161,15 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function getMetadata($key = null) { + $meta = stream_get_meta_data($this->buffer); + + if (!isset($key)) { + return $meta; + } + + if (isset($meta[$key])) { + return $meta[$key]; + } } /** @@ -203,13 +192,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function __toString() { - $string = ''; - - foreach ($this->videos as $file) { - $string .= $file->url; - } - - return $string; + return $this->getContents(); } /** @@ -232,23 +215,37 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function seek($offset, $whence = SEEK_SET) { - throw new RuntimeException('This stream is not seekable.'); + fseek($this->buffer, $offset, $whence); } /** - * Returns true if the stream is at the end of the stream. + * Returns true if the stream is at the end of the archive. * * @return bool */ public function eof() { - foreach ($this->videos as $file) { - if (!$file->complete) { - return false; - } - } + return $this->isComplete && feof($this->buffer); + } - return true; + /** + * Start streaming a new video. + * + * @param Video $video Video to stream + * + * @return void + */ + private 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] + ); } /** @@ -260,30 +257,30 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface */ public function read($count) { - if (isset($this->curVideo)) { - if (isset($this->curVideo->stream)) { - if (!$this->curVideo->stream->eof()) { - $this->stream_file_part($this->curVideo->stream->read($count)); - } elseif (!$this->curVideo->complete) { + // 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(); - $this->curVideo->complete = true; + + $video = next($this->videos); + if ($video) { + // Start streaming the next video. + $this->startVideoStream($video); + } else { + // No video left. + $this->finish(); + $this->isComplete = true; + } } else { - $this->curVideo = next($this->videos); + // Continue streaming the current video. + $this->stream_file_part($this->curVideoStream->read($count)); } } else { - $urls = $this->download->getURL($this->curVideo->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->curVideo->url, $this->format), - $contentLengthHeaders[0] - ); - - $this->curVideo->stream = $response->getBody(); + // Start streaming the first video. + $this->startVideoStream(current($this->videos)); } - } else { - $this->curVideo = current($this->videos); } return fread($this->buffer, $count); @@ -299,10 +296,8 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface if (is_resource($this->buffer)) { fclose($this->buffer); } - foreach ($this->videos as $file) { - if (is_resource($file->stream)) { - fclose($file->stream); - } + if (isset($this->curVideoStream)) { + $this->curVideoStream->close(); } } } diff --git a/classes/PlaylistArchiveVideo.php b/classes/PlaylistArchiveVideo.php deleted file mode 100644 index 4610427..0000000 --- a/classes/PlaylistArchiveVideo.php +++ /dev/null @@ -1,43 +0,0 @@ -url = $url; - } -} diff --git a/classes/VideoDownload.php b/classes/Video.php similarity index 64% rename from classes/VideoDownload.php rename to classes/Video.php index b32b4d8..4d52539 100644 --- a/classes/VideoDownload.php +++ b/classes/Video.php @@ -6,13 +6,17 @@ namespace Alltube; 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, this class can also contain a playlist. */ -class VideoDownload +class Video { /** * Config instance. @@ -21,30 +25,37 @@ 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 + */ + private $password; + /** * 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(); } /** @@ -56,10 +67,11 @@ class VideoDownload */ private 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,18 +82,15 @@ 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::getProp('list-extractors'))); } /** * Get a property from youtube-dl. * - * @param string $url URL to parse - * @param string $format Format * @param string $prop Property - * @param string $password Video password * * @throws PasswordException If the video is protected by a password and no password was specified * @throws Exception If the password is wrong @@ -89,23 +98,30 @@ class VideoDownload * * @return string */ - private function getProp($url = null, $format = null, $prop = 'dump-json', $password = null) + private function getProp($prop = 'dump-json') { + $config = Config::getInstance(); + $arguments = ['--'.$prop]; - if (isset($url)) { - $arguments[] = $url; - } - if (isset($format)) { - $arguments[] = '-f '.$format; - } - if (isset($password)) { - $arguments[] = '--video-password'; - $arguments[] = $password; + + // This function can also be called statically. + if (isset($this)) { + 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; + } } - $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()) { @@ -126,15 +142,41 @@ class VideoDownload /** * 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 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 boolean + */ + public function __isset($name) + { + return isset($this->getJson()->$name); } /** @@ -144,15 +186,11 @@ 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)); + $urls = explode("\n", $this->getProp('get-url')); if (empty($urls[0])) { throw new EmptyUrlException(_('youtube-dl returned an empty URL.')); @@ -164,32 +202,25 @@ class VideoDownload /** * 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 +228,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 stdClass $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 +246,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 +270,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 +281,6 @@ class VideoDownload /** * Get a process that runs avconv in order to convert a video. * - * @param stdClass $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 +292,6 @@ class VideoDownload * @return Process Process */ private function getAvconvProcess( - stdClass $video, $audioBitrate, $filetype = 'mp3', $audioOnly = true, @@ -317,9 +330,9 @@ class VideoDownload $this->config->avconv, '-v', $this->config->avconvVerbosity, ], - $this->getRtmpArguments($video), + $this->getRtmpArguments(), [ - '-i', $video->url, + '-i', $this->url, '-f', $filetype, '-b:a', $audioBitrate.'k', ], @@ -328,10 +341,10 @@ class VideoDownload 'pipe:1', ] ); - if ($video->url != '-') { + if ($this->url != '-') { //Vimeo needs a correct user-agent $arguments[] = '-user_agent'; - $arguments[] = $this->getProp(null, null, 'dump-user-agent'); + $arguments[] = $this->getProp('dump-user-agent'); } return new Process($arguments); @@ -340,34 +353,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 * - * @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,14 +389,12 @@ 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.'.'); @@ -398,8 +404,8 @@ class VideoDownload [ $this->config->avconv, '-v', $this->config->avconvVerbosity, - '-i', $video->url, - '-f', $video->ext, + '-i', $this->url, + '-f', $this->ext, '-c', 'copy', '-bsf:a', 'aac_adtstoasc', '-movflags', 'frag_keyframe+empty_moov', @@ -418,14 +424,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,13 +461,11 @@ 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() { $process = new Process( array_merge( @@ -465,10 +473,10 @@ class VideoDownload $this->config->avconv, '-v', $this->config->avconvVerbosity, ], - $this->getRtmpArguments($video), + $this->getRtmpArguments(), [ - '-i', $video->url, - '-f', $video->ext, + '-i', $this->url, + '-f', $this->ext, 'pipe:1', ] ) @@ -484,25 +492,21 @@ class VideoDownload /** * 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'); @@ -512,4 +516,29 @@ 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 Video($this->webpageUrl, $format, $this->password); + } + + /** + * Get a HTTP response containing the video. + * + * @return Response + */ + public function getHttpResponse() + { + $client = new Client(); + $urls = $this->getUrl(); + + return $client->request('GET', $urls[0], ['stream' => true]); + } } diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 9789583..f7cee45 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -11,7 +11,7 @@ use Alltube\Locale; use Alltube\LocaleManager; use Alltube\PasswordException; use Alltube\PlaylistArchiveStream; -use Alltube\VideoDownload; +use Alltube\Video; use Aura\Session\Segment; use Aura\Session\SessionFactory; use Exception; @@ -36,11 +36,11 @@ class FrontController private $config; /** - * VideoDownload instance. + * Current video. * - * @var VideoDownload + * @var Video */ - private $download; + private $video; /** * Slim dependency container. @@ -81,17 +81,11 @@ class FrontController * FrontController constructor. * * @param ContainerInterface $container Slim dependency container - * @param Config $config Config instance * @param array $cookies Cookie array */ - public function __construct(ContainerInterface $container, Config $config = null, array $cookies = []) + public function __construct(ContainerInterface $container, array $cookies = []) { - if (isset($config)) { - $this->config = $config; - } else { - $this->config = Config::getInstance(); - } - $this->download = new VideoDownload($this->config); + $this->config = Config::getInstance(); $this->container = $container; $this->view = $this->container->get('view'); $this->localeManager = $this->container->get('locale'); @@ -162,7 +156,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 '. @@ -206,44 +200,28 @@ class FrontController * * @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) + private function getConvertedAudioResponse(Request $request, Response $response) { - if (!isset($params['from'])) { - $params['from'] = ''; - } - if (!isset($params['to'])) { - $params['to'] = ''; - } + $from = $request->getQueryParam('from'); + $to = $request->getQueryParam('to'); $response = $response->withHeader( 'Content-Disposition', 'attachment; filename="'. - $this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"' + $this->video->getFileNameWithExtension('mp3').'"' ); $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'] - ); + $process = $this->video->getAudioStream($from, $to); } catch (Exception $e) { - $process = $this->download->getAudioStream( - $params['url'], - $this->defaultFormat, - $password, - $params['from'], - $params['to'] - ); + // Fallback to default format. + $this->video = $this->video->withFormat($this->defaultFormat); + $process = $this->video->getAudioStream($from, $to); } $response = $response->withBody(new Stream($process)); } @@ -256,31 +234,35 @@ class FrontController * * @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) + private function getAudioResponse(Request $request, Response $response) { try { - if ((isset($params['from']) && !empty($params['from'])) - || (isset($params['to']) && !empty($params['to'])) - ) { + // 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) { - return $this->getStream($params['url'], 'mp3', $response, $request, $password); + $this->video = $this->video->withFormat('mp3'); + + return $this->getStream($request, $response); } else { - $urls = $this->download->getURL($params['url'], 'mp3[protocol=https]/mp3[protocol=http]', $password); + $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]'); + + $urls = $this->video->getUrl(); return $response->withRedirect($urls[0]); } } catch (PasswordException $e) { return $this->password($request, $response); } catch (Exception $e) { - return $this->getConvertedAudioResponse($request, $response, $params, $password); + // If MP3 is not available, we convert it. + $this->video = $this->video->withFormat($this->defaultFormat); + + return $this->getConvertedAudioResponse($request, $response); } } @@ -289,35 +271,33 @@ class FrontController * * @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 getVideoResponse(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 (isset($video->entries)) { + if (isset($this->video->entries)) { $template = 'playlist.tpl'; } else { $template = 'video.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, + 'video' => $this->video, 'class' => 'video', 'title' => $title, 'description' => $description, @@ -341,21 +321,20 @@ class FrontController */ public function video(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($params['url']) && !empty($params['url'])) { + if (isset($url) && !empty($url)) { $password = $request->getParam('password'); if (isset($password)) { - $this->sessionSegment->setFlash($params['url'], $password); + $this->sessionSegment->setFlash($url, $password); } - if (isset($params['audio'])) { - return $this->getAudioResponse($request, $response, $params, $password); + + $this->video = new Video($url, $this->defaultFormat, $password); + + if ($request->getQueryParam('audio')) { + return $this->getAudioResponse($request, $response); } else { - return $this->getVideoResponse($request, $response, $params, $password); + return $this->getVideoResponse($request, $response); } } else { return $response->withRedirect($this->container->get('router')->pathFor('index')); @@ -392,39 +371,33 @@ class FrontController /** * 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) + private function getStream(Request $request, Response $response) { - $video = $this->download->getJSON($url, $format, $password); - if (isset($video->entries)) { - $stream = new PlaylistArchiveStream($this->config, $video, $format); + if (isset($this->video->entries)) { + $stream = new PlaylistArchiveStream($this->video); $response = $response->withHeader('Content-Type', 'application/x-tar'); $response = $response->withHeader( 'Content-Disposition', - 'attachment; filename="'.$video->title.'.tar"' + 'attachment; filename="'.$this->video->title.'.tar"' ); return $response->withBody($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); + } 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 { $client = new Client(); $stream = $client->request( 'GET', - $video->url, + $this->video->url, [ 'stream' => true, 'headers' => ['Range' => $request->getHeader('Range')], @@ -445,7 +418,7 @@ class FrontController $response = $response->withHeader( 'Content-Disposition', 'attachment; filename="'. - $this->download->getFilename($url, $format, $password).'"' + $this->video->getFilename().'"' ); return $response; @@ -454,19 +427,17 @@ class FrontController /** * 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) + 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->download->getRemuxStream($urls); + $stream = $this->video->getRemuxStream(); $response = $response->withHeader('Content-Type', 'video/x-matroska'); if ($request->isGet()) { $response = $response->withBody(new Stream($stream)); @@ -475,12 +446,7 @@ class FrontController return $response->withHeader( 'Content-Disposition', - 'attachment; filename="'.$this->download->getFileNameWithExtension( - 'mkv', - $webpageUrl, - $format, - $this->sessionSegment->getFlash($webpageUrl) - ) + 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv') ); } @@ -505,21 +471,15 @@ class FrontController * 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) + private function getRedirectResponse(Request $request, Response $response) { try { - $videoUrls = $this->download->getURL( - $url, - $format, - $this->sessionSegment->getFlash($url) - ); + $videoUrls = $this->video->getUrl(); } catch (EmptyUrlException $e) { /* If this happens it is probably a playlist @@ -528,15 +488,9 @@ class FrontController $videoUrls = []; } if (count($videoUrls) > 1) { - return $this->getRemuxStream($videoUrls, $format, $response, $request); + return $this->getRemuxStream($request, $response); } elseif ($this->config->stream) { - return $this->getStream( - $url, - $format, - $response, - $request, - $this->sessionSegment->getFlash($url) - ); + return $this->getStream($request, $response); } else { if (empty($videoUrls[0])) { throw new Exception(_("Can't find URL of video.")); @@ -551,33 +505,22 @@ class FrontController * * @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) + private function getConvertedResponse(Request $request, Response $response) { - $password = $request->getParam('password'); $response = $response->withHeader( 'Content-Disposition', 'attachment; filename="'. - $this->download->getFileNameWithExtension( - $params['customFormat'], - $params['url'], - $format, - $password - ).'"' + $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"' ); - $response = $response->withHeader('Content-Type', 'video/'.$params['customFormat']); + $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat')); if ($request->isGet() || $request->isPost()) { - $process = $this->download->getConvertedStream( - $params['url'], - $format, - $params['customBitrate'], - $params['customFormat'], - $password + $process = $this->video->getConvertedStream( + $request->getQueryParam('customBitrate'), + $request->getQueryParam('customFormat') ); $response = $response->withBody(new Stream($process)); } @@ -595,18 +538,21 @@ class FrontController */ public function redirect(Request $request, Response $response) { - $params = $request->getQueryParams(); $format = $this->getFormat($request); - if (isset($params['url'])) { + $url = $request->getQueryParam('url'); + + if (isset($url)) { + $this->video = new Video($url, $format, $this->sessionSegment->getFlash($url)); + try { if ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { - return $this->getConvertedResponse($request, $response, $params, $format); + return $this->getConvertedResponse($request, $response); } - return $this->getRedirectResponse($params['url'], $format, $response, $request); + return $this->getRedirectResponse($request, $response); } catch (PasswordException $e) { return $response->withRedirect( - $this->container->get('router')->pathFor('video').'?url='.urlencode($params['url']) + $this->container->get('router')->pathFor('video').'?url='.urlencode($url) ); } catch (Exception $e) { $response->getBody()->write($e->getMessage()); @@ -628,16 +574,14 @@ class FrontController */ public function json(Request $request, Response $response) { - $params = $request->getQueryParams(); $format = $this->getFormat($request); - if (isset($params['url'])) { + $url = $request->getQueryParam('url'); + + if (isset($url)) { try { - return $response->withJson( - $this->download->getJSON( - $params['url'], - $format - ) - ); + $this->video = new Video($url, $format); + + return $response->withJson($this->video->getJson()); } catch (Exception $e) { return $response->withJson(['error' => $e->getMessage()]) ->withStatus(500); diff --git a/index.php b/index.php index c42b96e..77f8a85 100644 --- a/index.php +++ b/index.php @@ -14,6 +14,10 @@ if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.ph die; } +if (is_file(__DIR__.'/config/config.yml')) { + Config::setFile(__DIR__.'/config/config.yml'); +} + $app = new App(); $container = $app->getContainer(); $config = Config::getInstance(); @@ -28,7 +32,7 @@ if (!class_exists('Locale')) { $container['locale'] = new LocaleManager($_COOKIE); $app->add(new LocaleMiddleware($container)); -$controller = new FrontController($container, null, $_COOKIE); +$controller = new FrontController($container, $_COOKIE); $container['errorHandler'] = [$controller, 'error']; diff --git a/tests/BaseTest.php b/tests/BaseTest.php new file mode 100644 index 0000000..c70756b --- /dev/null +++ b/tests/BaseTest.php @@ -0,0 +1,42 @@ +config = Config::getInstance('config/config_test.yml'); - } - - /** - * Destroy variables created by setUp(). - * - * @return void - */ - protected function tearDown() - { - Config::destroyInstance(); + $this->config = Config::getInstance(); } /** @@ -70,27 +60,79 @@ class ConfigTest extends TestCase } /** - * Test the getInstance function with a missing config file. + * Test the setFile function. + * + * @return void + */ + public function testSetFile() + { + if (PHP_OS == 'WINNT') { + $configFile = 'config_test_windows.yml'; + } else { + $configFile = 'config_test.yml'; + } + + $this->assertNull(Config::setFile(__DIR__.'/../config/'.$configFile)); + } + + /** + * 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() + { + 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']); + } + + /** * Test the getInstance function with the CONVERT and PYTHON environment variables. * @@ -100,11 +142,8 @@ class ConfigTest extends TestCase { Config::destroyInstance(); putenv('CONVERT=1'); - putenv('PYTHON=foo'); - $config = Config::getInstance('config/config_test.yml'); + $config = Config::getInstance(); $this->assertEquals($config->convert, true); - $this->assertEquals($config->python, 'foo'); putenv('CONVERT'); - putenv('PYTHON'); } } diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index a7a9c21..f8cfa02 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -19,7 +19,7 @@ use Slim\Http\Response; /** * Unit tests for the FrontController class. */ -class FrontControllerTest extends TestCase +class FrontControllerTest extends BaseTest { /** * Slim dependency container. @@ -61,19 +61,15 @@ class FrontControllerTest extends TestCase */ protected function setUp() { + parent::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(); - 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->controller = new FrontController($this->container); $this->container['router']->map(['GET'], '/', [$this->controller, 'index']) ->setName('index'); @@ -87,32 +83,17 @@ class FrontControllerTest extends TestCase ->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) + private function getRequestResult($request, array $params) { - if (isset($config)) { - $controller = new FrontController($this->container, $config); - } else { - $controller = $this->controller; - } - - return $controller->$request( + return $this->controller->$request( $this->request->withQueryParams($params), $this->response ); @@ -123,13 +104,12 @@ class FrontControllerTest extends TestCase * * @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) + private function assertRequestIsOk($request, array $params = []) { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isOk()); + $this->assertTrue($this->getRequestResult($request, $params)->isOk()); } /** @@ -137,13 +117,12 @@ class FrontControllerTest extends TestCase * * @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) + private function assertRequestIsRedirect($request, array $params = []) { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isRedirect()); + $this->assertTrue($this->getRequestResult($request, $params)->isRedirect()); } /** @@ -151,13 +130,12 @@ class FrontControllerTest extends TestCase * * @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) + private function assertRequestIsServerError($request, array $params = []) { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isServerError()); + $this->assertTrue($this->getRequestResult($request, $params)->isServerError()); } /** @@ -165,13 +143,12 @@ class FrontControllerTest extends TestCase * * @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) + private function assertRequestIsClientError($request, array $params = []) { - $this->assertTrue($this->getRequestResult($request, $params, $config)->isClientError()); + $this->assertTrue($this->getRequestResult($request, $params)->isClientError()); } /** @@ -181,20 +158,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 +168,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)); } /** @@ -354,12 +317,12 @@ class FrontControllerTest extends TestCase */ public function testVideoWithStream() { - $this->config->stream = true; - $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'], $this->config); + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); $this->assertRequestIsOk( 'video', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true], - $this->config + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true] ); } @@ -427,11 +390,11 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithStream() { - $this->config->stream = true; + Config::setOptions(['stream' => true]); + $this->assertRequestIsOk( 'redirect', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'], - $this->config + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'] ); } @@ -445,14 +408,15 @@ class FrontControllerTest extends TestCase if (getenv('CI')) { $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.'); } - $this->config->stream = true; + + Config::setOptions(['stream' => true]); + $this->assertRequestIsOk( 'redirect', [ 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', 'format' => 'hls-2176', - ], - $this->config + ] ); } @@ -465,11 +429,11 @@ class FrontControllerTest extends TestCase { $this->markTestIncomplete('We need to find another RTMP video.'); - $this->config->stream = true; + Config::setOptions(['stream' => true]); + $this->assertRequestIsOk( 'redirect', - ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'], - $this->config + ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'] ); } @@ -480,14 +444,14 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithRemux() { - $this->config->remux = true; + Config::setOptions(['remux' => true]); + $this->assertRequestIsOk( 'redirect', [ 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'bestvideo+bestaudio', - ], - $this->config + ] ); } @@ -552,11 +516,11 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithPlaylist() { - $this->config->stream = true; + Config::setOptions(['stream' => true]); + $this->assertRequestIsOk( 'redirect', - ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'], - $this->config + ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] ); } @@ -567,7 +531,8 @@ class FrontControllerTest extends TestCase */ public function testRedirectWithAdvancedConversion() { - $this->config->convertAdvanced = true; + Config::setOptions(['convertAdvanced' => true]); + $this->assertRequestIsOk( 'redirect', [ @@ -576,8 +541,7 @@ class FrontControllerTest extends TestCase 'customConvert' => 'on', 'customBitrate' => 32, 'customFormat' => 'flv', - ], - $this->config + ] ); } diff --git a/tests/LocaleManagerTest.php b/tests/LocaleManagerTest.php index 9bf9837..aab0463 100644 --- a/tests/LocaleManagerTest.php +++ b/tests/LocaleManagerTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; /** * Unit tests for the LocaleManagerTest class. */ -class LocaleManagerTest extends TestCase +class LocaleManagerTest extends BaseTest { /** * LocaleManager class instance. diff --git a/tests/LocaleMiddlewareTest.php b/tests/LocaleMiddlewareTest.php index f7455fd..a8f45b4 100644 --- a/tests/LocaleMiddlewareTest.php +++ b/tests/LocaleMiddlewareTest.php @@ -17,7 +17,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 ba18b84..f425fe0 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase; /** * Unit tests for the LocaleTest class. */ -class LocaleTest extends TestCase +class LocaleTest extends BaseTest { /** * Locale class instance. diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index 0f07b2e..fc34fe3 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -6,6 +6,7 @@ namespace Alltube\Test; use Alltube\Config; +use Alltube\Video; use Alltube\PlaylistArchiveStream; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -14,7 +15,7 @@ use stdClass; /** * Unit tests for the ViewFactory class. */ -class PlaylistArchiveStreamTest extends TestCase +class PlaylistArchiveStreamTest extends BaseTest { /** * PlaylistArchiveStream instance. @@ -28,19 +29,11 @@ class PlaylistArchiveStreamTest extends TestCase */ protected function setUp() { - if (PHP_OS == 'WINNT') { - $configFile = 'config_test_windows.yml'; - } else { - $configFile = 'config_test.yml'; - } + parent::setUp(); - $entry = new stdClass(); - $entry->url = 'BaW_jenozKc'; + $video = new Video('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); - $video = new stdClass(); - $video->entries = [$entry, $entry]; - - $this->stream = new PlaylistArchiveStream(Config::getInstance('config/'.$configFile), $video, 'worst'); + $this->stream = new PlaylistArchiveStream($video); } /** @@ -57,11 +50,10 @@ class PlaylistArchiveStreamTest extends TestCase * Test the write() function. * * @return void - * @expectedException RuntimeException */ public function testWrite() { - $this->stream->write('foo'); + $this->assertNull($this->stream->write('foo')); } /** @@ -78,11 +70,12 @@ class PlaylistArchiveStreamTest extends TestCase * Test the seek() function. * * @return void - * @expectedException RuntimeException */ public function testSeek() { - $this->stream->seek(42); + $this->stream->write('foobar'); + $this->stream->seek(3); + $this->assertEquals(3, $this->stream->tell()); } /** @@ -92,13 +85,9 @@ class PlaylistArchiveStreamTest extends TestCase */ public function testRead() { - while (!$this->stream->eof()) { - $result = $this->stream->read(8192); - $this->assertInternalType('string', $result); - if (is_string($result)) { - $this->assertLessThanOrEqual(8192, strlen($result)); - } - } + $result = $this->stream->read(8192); + $this->assertInternalType('string', $result); + $this->assertLessThanOrEqual(8192, strlen($result)); } /** @@ -128,18 +117,18 @@ class PlaylistArchiveStreamTest extends TestCase */ public function testIsSeekable() { - $this->assertFalse($this->stream->isSeekable()); + $this->assertTrue($this->stream->isSeekable()); } /** * Test the rewind() function. * * @return void - * @expectedException RuntimeException */ public function testRewind() { $this->stream->rewind(); + $this->assertEquals(0, $this->stream->tell()); } /** @@ -149,7 +138,7 @@ class PlaylistArchiveStreamTest extends TestCase */ public function testIsWritable() { - $this->assertFalse($this->stream->isWritable()); + $this->assertTrue($this->stream->isWritable()); } /** @@ -179,7 +168,7 @@ class PlaylistArchiveStreamTest extends TestCase */ public function testGetMetadata() { - $this->assertNull($this->stream->getMetadata()); + $this->assertInternalType('array', $this->stream->getMetadata()); } /** diff --git a/tests/UglyRouterTest.php b/tests/UglyRouterTest.php index 541b78d..fb26a61 100644 --- a/tests/UglyRouterTest.php +++ b/tests/UglyRouterTest.php @@ -13,7 +13,7 @@ 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/VideoStubsTest.php similarity index 59% rename from tests/VideoDownloadStubsTest.php rename to tests/VideoStubsTest.php index c97f5f0..07ca0fe 100644 --- a/tests/VideoDownloadStubsTest.php +++ b/tests/VideoStubsTest.php @@ -1,59 +1,40 @@ config = Config::getInstance('config/'.$configFile); - $this->download = new VideoDownload($this->config); - $this->url = 'https://www.youtube.com/watch?v=XJC9_JkzugE'; + $this->video = new Video('https://www.youtube.com/watch?v=XJC9_JkzugE'); } /** @@ -74,7 +55,7 @@ class VideoDownloadStubsTest extends TestCase */ public function testGetAudioStreamWithPopenError() { - $this->download->getAudioStream($this->url, 'best'); + $this->video->getAudioStream(); } /** @@ -85,7 +66,7 @@ class VideoDownloadStubsTest extends TestCase */ public function testGetM3uStreamWithPopenError() { - $this->download->getM3uStream($this->download->getJSON($this->url, 'best')); + $this->video->getM3uStream(); } /** @@ -96,7 +77,7 @@ class VideoDownloadStubsTest extends TestCase */ public function testGetRtmpStreamWithPopenError() { - $this->download->getRtmpStream($this->download->getJSON($this->url, 'best')); + $this->video->getRtmpStream(); } /** @@ -107,7 +88,8 @@ class VideoDownloadStubsTest extends TestCase */ public function testGetRemuxStreamWithPopenError() { - $this->download->getRemuxStream([$this->url, $this->url]); + $video = $this->video->withFormat('bestvideo+bestaudio'); + $video->getRemuxStream(); } /** @@ -118,6 +100,6 @@ class VideoDownloadStubsTest extends TestCase */ public function testGetConvertedStreamWithPopenError() { - $this->download->getConvertedStream($this->url, 'best', 32, 'flv'); + $this->video->getConvertedStream(32, 'flv'); } } diff --git a/tests/VideoDownloadTest.php b/tests/VideoTest.php similarity index 68% rename from tests/VideoDownloadTest.php rename to tests/VideoTest.php index a16c15b..64f125f 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoTest.php @@ -1,92 +1,32 @@ 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,61 +39,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() { if (getenv('CI')) { $this->markTestSkipped('Travis is blacklisted by Vimeo.'); } - $videoURL = $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl'); - $this->assertContains('vimeocdn.com', $videoURL[0]); + + $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 */ - public function testGetURLWithMissingPassword() + public function testgetUrlWithMissingPassword() { if (getenv('CI')) { $this->markTestSkipped('Travis is blacklisted by Vimeo.'); } - $this->download->getURL('http://vimeo.com/68375962'); + + $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() { if (getenv('CI')) { $this->markTestSkipped('Travis is blacklisted by Vimeo.'); } - $this->download->getURL('http://vimeo.com/68375962', null, 'foo'); + + $video = new Video('http://vimeo.com/68375962', 'best', 'foo'); + $video->getUrl(); } /** - * Test getURL function errors. + * Test getUrl function errors. * * @param string $url URL * @@ -161,9 +110,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(); } /** @@ -298,9 +248,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); @@ -318,9 +269,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(); } /** @@ -338,8 +290,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); } /** @@ -353,25 +305,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(); } /** @@ -385,9 +320,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()); } /** @@ -402,9 +336,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(); } /** @@ -419,7 +354,8 @@ class VideoDownloadTest extends TestCase */ public function testGetAudioStreamM3uError($url, $format) { - $this->download->getAudioStream($url, $format); + $video = new Video($url, $format); + $video->getAudioStream(); } /** @@ -430,7 +366,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(); } /** @@ -441,10 +382,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(); } /** @@ -471,11 +413,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()); } /** @@ -489,10 +428,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(); } /** @@ -508,11 +461,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()); } /** @@ -527,10 +478,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']); + + $video = new Video($url, $format); + $video->getM3uStream(); } /** @@ -544,7 +495,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')); } /** @@ -559,6 +511,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..df1358c 100644 --- a/tests/ViewFactoryTest.php +++ b/tests/ViewFactoryTest.php @@ -15,7 +15,7 @@ use Slim\Views\Smarty; /** * Unit tests for the ViewFactory class. */ -class ViewFactoryTest extends TestCase +class ViewFactoryTest extends BaseTest { /** * Test the create() function. From 06a631c8929f12f0fefca4ce8d3e2fe42177b3bb Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 18:35:24 +0200 Subject: [PATCH 38/73] fixup! refactor: New Video class --- classes/Config.php | 7 ++++--- classes/PlaylistArchiveStream.php | 3 +-- classes/Video.php | 30 ++++++++++++++++------------- tests/BaseTest.php | 5 ----- tests/ConfigTest.php | 2 -- tests/FrontControllerTest.php | 1 - tests/LocaleManagerTest.php | 1 - tests/LocaleMiddlewareTest.php | 1 - tests/LocaleTest.php | 1 - tests/PlaylistArchiveStreamTest.php | 6 +----- tests/UglyRouterTest.php | 1 - tests/VideoStubsTest.php | 2 -- tests/VideoTest.php | 2 -- tests/ViewFactoryTest.php | 1 - 14 files changed, 23 insertions(+), 40 deletions(-) diff --git a/classes/Config.php b/classes/Config.php index 5edf12b..1e8e9a1 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -141,9 +141,10 @@ class Config /** * Throw an exception if some of the options are invalid. * - * @return void * @throws Exception If youtube-dl is missing * @throws Exception If Python is missing + * + * @return void */ private function validateOptions() { @@ -223,8 +224,8 @@ class Config /** * Manually set some options. * - * @param array $options Options (see `config/config.example.yml` for available options) - * @param boolean $update True to update an existing instance + * @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) { diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index a702de1..4d9c3c0 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -8,8 +8,6 @@ namespace Alltube; use Barracuda\ArchiveStream\TarArchive; use GuzzleHttp\Psr7\Stream; use Psr\Http\Message\StreamInterface; -use RuntimeException; -use stdClass; /** * Class used to create a Tar archive from playlists and stream it to the browser. @@ -41,6 +39,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface /** * True if the archive is complete. + * * @var bool */ private $isComplete = false; diff --git a/classes/Video.php b/classes/Video.php index 4d52539..9c60a4b 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -26,19 +26,22 @@ class Video private $config; /** - * URL of the page containing the video + * URL of the page containing the video. + * * @var string */ private $webpageUrl; /** - * Requested video format + * Requested video format. + * * @var string */ private $requestedFormat; /** - * Password + * Password. + * * @var string */ private $password; @@ -68,6 +71,7 @@ class Video private function getProcess(array $arguments) { $config = Config::getInstance(); + return new Process( array_merge( [$config->python, $config->youtubedl], @@ -90,7 +94,7 @@ class Video /** * Get a property from youtube-dl. * - * @param string $prop Property + * @param string $prop Property * * @throws PasswordException If the video is protected by a password and no password was specified * @throws Exception If the password is wrong @@ -172,7 +176,7 @@ class Video * * @param string $name Property * - * @return boolean + * @return bool */ public function __isset($name) { @@ -281,11 +285,11 @@ class Video /** * Get a process that runs avconv in order to convert a video. * - * @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 - * @param string $from Start the conversion at this time - * @param string $to End the conversion at this time + * @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 + * @param string $from Start the conversion at this time + * @param string $to End the conversion at this time * * @throws Exception If avconv/ffmpeg is missing * @@ -353,8 +357,8 @@ class Video /** * Get audio stream of converted video. * - * @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 an M3U8 video * @throws Exception If the popen stream was not created correctly @@ -526,7 +530,7 @@ class Video */ public function withFormat($format) { - return new Video($this->webpageUrl, $format, $this->password); + return new self($this->webpageUrl, $format, $this->password); } /** diff --git a/tests/BaseTest.php b/tests/BaseTest.php index c70756b..ab76316 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -6,18 +6,13 @@ namespace Alltube\Test; use Alltube\Config; -use Alltube\Video; -use Alltube\PlaylistArchiveStream; use PHPUnit\Framework\TestCase; -use RuntimeException; -use stdClass; /** * Unit tests for the ViewFactory class. */ abstract class BaseTest extends TestCase { - /** * Prepare tests. */ diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index ffedca2..ac6189c 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -6,7 +6,6 @@ namespace Alltube\Test; use Alltube\Config; -use PHPUnit\Framework\TestCase; /** * Unit tests for the Config class. @@ -132,7 +131,6 @@ class ConfigTest extends BaseTest Config::setOptions(['python' => 'foo']); } - /** * Test the getInstance function with the CONVERT and PYTHON environment variables. * diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index f8cfa02..5193f0e 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -10,7 +10,6 @@ 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; diff --git a/tests/LocaleManagerTest.php b/tests/LocaleManagerTest.php index aab0463..6ebb1b2 100644 --- a/tests/LocaleManagerTest.php +++ b/tests/LocaleManagerTest.php @@ -7,7 +7,6 @@ namespace Alltube\Test; use Alltube\Locale; use Alltube\LocaleManager; -use PHPUnit\Framework\TestCase; /** * Unit tests for the LocaleManagerTest class. diff --git a/tests/LocaleMiddlewareTest.php b/tests/LocaleMiddlewareTest.php index a8f45b4..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; diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php index f425fe0..6e429fd 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -6,7 +6,6 @@ namespace Alltube\Test; use Alltube\Locale; -use PHPUnit\Framework\TestCase; /** * Unit tests for the LocaleTest class. diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index fc34fe3..63980f2 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -5,12 +5,8 @@ namespace Alltube\Test; -use Alltube\Config; -use Alltube\Video; use Alltube\PlaylistArchiveStream; -use PHPUnit\Framework\TestCase; -use RuntimeException; -use stdClass; +use Alltube\Video; /** * Unit tests for the ViewFactory class. diff --git a/tests/UglyRouterTest.php b/tests/UglyRouterTest.php index fb26a61..3609db8 100644 --- a/tests/UglyRouterTest.php +++ b/tests/UglyRouterTest.php @@ -6,7 +6,6 @@ namespace Alltube\Test; use Alltube\UglyRouter; -use PHPUnit\Framework\TestCase; use Slim\Http\Environment; use Slim\Http\Request; diff --git a/tests/VideoStubsTest.php b/tests/VideoStubsTest.php index 07ca0fe..0b96139 100644 --- a/tests/VideoStubsTest.php +++ b/tests/VideoStubsTest.php @@ -5,11 +5,9 @@ namespace Alltube\Test; -use Alltube\Config; use Alltube\Video; use Mockery; use phpmock\mockery\PHPMockery; -use PHPUnit\Framework\TestCase; /** * Unit tests for the Video class. diff --git a/tests/VideoTest.php b/tests/VideoTest.php index 64f125f..48bb7e6 100644 --- a/tests/VideoTest.php +++ b/tests/VideoTest.php @@ -7,14 +7,12 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\Video; -use PHPUnit\Framework\TestCase; /** * Unit tests for the Video class. */ class VideoTest extends BaseTest { - /** * Test getExtractors function. * diff --git a/tests/ViewFactoryTest.php b/tests/ViewFactoryTest.php index df1358c..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; From 51eaf192b1be422352de7afa732cf4922c5f9c7e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 18:49:05 +0200 Subject: [PATCH 39/73] refactor: Don't mix static and dynamic methods --- classes/Video.php | 57 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/classes/Video.php b/classes/Video.php index 9c60a4b..f1247e5 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -42,7 +42,7 @@ class Video /** * Password. * - * @var string + * @var string|null */ private $password; @@ -68,7 +68,7 @@ class Video * * @return Process */ - private function getProcess(array $arguments) + private static function getProcess(array $arguments) { $config = Config::getInstance(); @@ -88,41 +88,24 @@ class Video * */ public static function getExtractors() { - return explode("\n", trim(self::getProp('list-extractors'))); + return explode("\n", trim(self::callYoutubedl(['--list-extractors']))); } /** - * Get a property from youtube-dl. + * Call youtube-dl. * - * @param string $prop Property + * @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($prop = 'dump-json') + private static function callYoutubedl(array $arguments) { $config = Config::getInstance(); - $arguments = ['--'.$prop]; - - // This function can also be called statically. - if (isset($this)) { - 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; - } - } - $process = self::getProcess($arguments); //This is needed by the openload extractor because it runs PhantomJS $process->setEnv(['PATH'=>$config->phantomjsDir]); @@ -143,6 +126,32 @@ class Video } } + /** + * 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. * From 4fa604eec3dc0296ba556baff744fa7a8c750222 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 18:56:02 +0200 Subject: [PATCH 40/73] test(phpunit): Fix ConfigTest setup --- tests/ConfigTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index ac6189c..5cf850c 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -24,6 +24,8 @@ class ConfigTest extends BaseTest */ protected function setUp() { + parent::setUp(); + $this->config = Config::getInstance(); } From dc4eafe33f724d33412f108ef60e8373a406b793 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 19:10:37 +0200 Subject: [PATCH 41/73] docs: Document magic properties --- classes/Video.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/classes/Video.php b/classes/Video.php index f1247e5..e39decb 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -14,7 +14,14 @@ use Symfony\Component\Process\Process; /** * Extract info about videos. * - * Due to the way youtube-dl, this class can also contain a playlist. + * 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) */ class Video { @@ -46,6 +53,13 @@ class Video */ private $password; + /** + * JSON object returned by youtube-dl. + * + * @var stdClass + */ + private $json; + /** * VideoDownload constructor. * From 28b99861c292604167fe4689d54e8a2f693a5ab2 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 19:14:23 +0200 Subject: [PATCH 42/73] test(phpunit): Fix ConfigTest on Windows --- tests/BaseTest.php | 16 +++++++++++++--- tests/ConfigTest.php | 9 ++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/BaseTest.php b/tests/BaseTest.php index ab76316..d3f7796 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -14,9 +14,11 @@ use PHPUnit\Framework\TestCase; abstract class BaseTest extends TestCase { /** - * Prepare tests. + * Get the config file used in tests. + * + * @return string Path to file */ - protected function setUp() + protected function getConfigFile() { if (PHP_OS == 'WINNT') { $configFile = 'config_test_windows.yml'; @@ -24,7 +26,15 @@ abstract class BaseTest extends TestCase $configFile = 'config_test.yml'; } - Config::setFile(__DIR__.'/../config/'.$configFile); + return __DIR__.'/../config/'.$configFile; + } + + /** + * Prepare tests. + */ + protected function setUp() + { + Config::setFile($this->getConfigFile()); } /** diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 5cf850c..13cf4c6 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -67,13 +67,7 @@ class ConfigTest extends BaseTest */ public function testSetFile() { - if (PHP_OS == 'WINNT') { - $configFile = 'config_test_windows.yml'; - } else { - $configFile = 'config_test.yml'; - } - - $this->assertNull(Config::setFile(__DIR__.'/../config/'.$configFile)); + $this->assertNull(Config::setFile($this->getConfigFile())); } /** @@ -142,6 +136,7 @@ class ConfigTest extends BaseTest { Config::destroyInstance(); putenv('CONVERT=1'); + Config::setFile($this->getConfigFile()); $config = Config::getInstance(); $this->assertEquals($config->convert, true); putenv('CONVERT'); From d4e8e32cd6c2e70c47fbede2ccb80b704131647d Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 19:28:58 +0200 Subject: [PATCH 43/73] test(phpunit): Disable testSetOptionsWithoutUpdate() on AppVeyor --- tests/ConfigTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 13cf4c6..e8141c6 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -100,6 +100,12 @@ class ConfigTest extends BaseTest */ 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'); From 61cb73dc5916b9cf0c64f9ce2970433bbb99d5ce Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 19:47:30 +0200 Subject: [PATCH 44/73] refactor: Fix some types and unused variables --- classes/PlaylistArchiveStream.php | 2 +- classes/Video.php | 14 ++++++++------ controllers/FrontController.php | 1 - tests/ConfigTest.php | 8 +++++--- tests/FrontControllerTest.php | 7 ------- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 4d9c3c0..3675595 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -156,7 +156,7 @@ class PlaylistArchiveStream extends TarArchive implements StreamInterface * * @param string $key string $key Specific metadata to retrieve. * - * @return null + * @return array|mixed|null */ public function getMetadata($key = null) { diff --git a/classes/Video.php b/classes/Video.php index e39decb..63a1913 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -16,12 +16,14 @@ use Symfony\Component\Process\Process; * * 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 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) */ class Video { diff --git a/controllers/FrontController.php b/controllers/FrontController.php index f7cee45..b420b95 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -442,7 +442,6 @@ class FrontController if ($request->isGet()) { $response = $response->withBody(new Stream($stream)); } - $webpageUrl = $request->getQueryParam('url'); return $response->withHeader( 'Content-Disposition', diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index e8141c6..6480521 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -36,8 +36,9 @@ class ConfigTest extends BaseTest */ public function testGetInstance() { - $this->assertEquals($this->config->convert, false); - $this->assertConfig($this->config); + $config = Config::getInstance(); + $this->assertEquals($config->convert, false); + $this->assertConfig($config); } /** @@ -67,7 +68,8 @@ class ConfigTest extends BaseTest */ public function testSetFile() { - $this->assertNull(Config::setFile($this->getConfigFile())); + Config::setFile($this->getConfigFile()); + $this->assertConfig($this->config); } /** diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 5193f0e..30f703b 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -48,13 +48,6 @@ class FrontControllerTest extends BaseTest */ private $controller; - /** - * Config class instance. - * - * @var Config - */ - private $config; - /** * Prepare tests. */ From 3ca84e144eab1a6e98ad02eedff4cd421dc38560 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 21:36:14 +0200 Subject: [PATCH 45/73] refactor: We don't need this check anymore --- classes/Video.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/classes/Video.php b/classes/Video.php index 63a1913..1fe1012 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -370,11 +370,10 @@ class Video 'pipe:1', ] ); - if ($this->url != '-') { - //Vimeo needs a correct user-agent - $arguments[] = '-user_agent'; - $arguments[] = $this->getProp('dump-user-agent'); - } + + //Vimeo needs a correct user-agent + $arguments[] = '-user_agent'; + $arguments[] = $this->getProp('dump-user-agent'); return new Process($arguments); } From ebed2cea8842b58651a4f9397f1072caf2587273 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:19:44 +0200 Subject: [PATCH 46/73] refactor: Avoid varible name conflict --- templates/playlist.tpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/playlist.tpl b/templates/playlist.tpl index 84134b1..bd70a6a 100644 --- a/templates/playlist.tpl +++ b/templates/playlist.tpl @@ -9,26 +9,26 @@ {if $config->stream} webpage_url}" class="downloadBtn">Download everything {/if} -{foreach $video->entries as $video} +{foreach $video->entries as $entry}

- {if !isset($video->title)} - {if $video->ie_key == YoutubePlaylist} + {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} From 226f1b8380326a418df655821f9c16683c0eda17 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:20:05 +0200 Subject: [PATCH 47/73] fix: Don't use the $url property It is not reliable --- classes/Video.php | 6 +++--- controllers/FrontController.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/classes/Video.php b/classes/Video.php index 1fe1012..a53b344 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -361,7 +361,7 @@ class Video ], $this->getRtmpArguments(), [ - '-i', $this->url, + '-i', $this->getUrl(), '-f', $filetype, '-b:a', $audioBitrate.'k', ], @@ -432,7 +432,7 @@ class Video [ $this->config->avconv, '-v', $this->config->avconvVerbosity, - '-i', $this->url, + '-i', $this->getUrl(), '-f', $this->ext, '-c', 'copy', '-bsf:a', 'aac_adtstoasc', @@ -503,7 +503,7 @@ class Video ], $this->getRtmpArguments(), [ - '-i', $this->url, + '-i', $this->getUrl(), '-f', $this->ext, 'pipe:1', ] diff --git a/controllers/FrontController.php b/controllers/FrontController.php index b420b95..4af49fd 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -397,7 +397,7 @@ class FrontController $client = new Client(); $stream = $client->request( 'GET', - $this->video->url, + $this->video->getUrl(), [ 'stream' => true, 'headers' => ['Range' => $request->getHeader('Range')], From e93ab7ed139f90c8198bb50873ca6ad173590fe5 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:34:45 +0200 Subject: [PATCH 48/73] feat: Use ZIP files for playlist archives It is more widely supported and could be later used for #194 --- classes/PlaylistArchiveStream.php | 6 +++--- controllers/FrontController.php | 4 ++-- phpstan.neon | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/classes/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 3675595..78f716d 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -5,16 +5,16 @@ namespace Alltube; -use Barracuda\ArchiveStream\TarArchive; +use Barracuda\ArchiveStream\ZipArchive; use GuzzleHttp\Psr7\Stream; use Psr\Http\Message\StreamInterface; /** - * Class used to create a Tar archive from playlists and stream it to the browser. + * Class used to create a Zip archive from playlists and stream it to the browser. * * @link https://github.com/php-fig/http-message/blob/master/src/StreamInterface.php */ -class PlaylistArchiveStream extends TarArchive implements StreamInterface +class PlaylistArchiveStream extends ZipArchive implements StreamInterface { /** * videos to add in the archive. diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 4af49fd..8b1758e 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -380,10 +380,10 @@ class FrontController { if (isset($this->video->entries)) { $stream = new PlaylistArchiveStream($this->video); - $response = $response->withHeader('Content-Type', 'application/x-tar'); + $response = $response->withHeader('Content-Type', 'application/zip'); $response = $response->withHeader( 'Content-Disposition', - 'attachment; filename="'.$this->video->title.'.tar"' + 'attachment; filename="'.$this->video->title.'.zip"' ); return $response->withBody($stream); diff --git a/phpstan.neon b/phpstan.neon index e7225f9..0f1af62 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,4 @@ parameters: ignoreErrors: # The Archive constructor messes up the output buffering. - - '#Alltube\\PlaylistArchiveStream::__construct\(\) does not call parent constructor from Barracuda\\ArchiveStream\\TarArchive\.#' + - '#Alltube\\PlaylistArchiveStream::__construct\(\) does not call parent constructor from Barracuda\\ArchiveStream\\ZipArchive\.#' From 97a7830196da9a94e85adcf5dfa5e94837004acf Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:38:27 +0200 Subject: [PATCH 49/73] refactor: Reuse Video::getHttpResponse() where possible --- classes/Video.php | 4 ++-- controllers/FrontController.php | 12 ++---------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/classes/Video.php b/classes/Video.php index a53b344..02f308c 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -562,11 +562,11 @@ class Video * * @return Response */ - public function getHttpResponse() + public function getHttpResponse(array $headers = []) { $client = new Client(); $urls = $this->getUrl(); - return $client->request('GET', $urls[0], ['stream' => true]); + return $client->request('GET', $urls[0], ['stream' => true, 'headers' => $headers]); } } diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 8b1758e..1585f4f 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -15,7 +15,6 @@ use Alltube\Video; use Aura\Session\Segment; use Aura\Session\SessionFactory; use Exception; -use GuzzleHttp\Client; use Psr\Container\ContainerInterface; use Slim\Container; use Slim\Http\Request; @@ -394,15 +393,8 @@ class FrontController $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); $body = new Stream($this->video->getM3uStream()); } else { - $client = new Client(); - $stream = $client->request( - 'GET', - $this->video->getUrl(), - [ - 'stream' => true, - 'headers' => ['Range' => $request->getHeader('Range')], - ] - ); + $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')); From 4db519c2ef0fae3346ccf6e39d88820e0aa1614d Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:40:54 +0200 Subject: [PATCH 50/73] fixup! fix: Don't use the $url property --- classes/Video.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/classes/Video.php b/classes/Video.php index 02f308c..079cf96 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -354,6 +354,8 @@ class Video $afterArguments[] = $to; } + $urls = $this->getUrl(); + $arguments = array_merge( [ $this->config->avconv, @@ -361,7 +363,7 @@ class Video ], $this->getRtmpArguments(), [ - '-i', $this->getUrl(), + '-i', $urls[0], '-f', $filetype, '-b:a', $audioBitrate.'k', ], @@ -428,11 +430,13 @@ class Video 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', $this->getUrl(), + '-i', $urls[0], '-f', $this->ext, '-c', 'copy', '-bsf:a', 'aac_adtstoasc', @@ -495,6 +499,8 @@ class Video */ public function getRtmpStream() { + $urls = $this->getUrl(); + $process = new Process( array_merge( [ @@ -503,7 +509,7 @@ class Video ], $this->getRtmpArguments(), [ - '-i', $this->getUrl(), + '-i', $urls[0], '-f', $this->ext, 'pipe:1', ] From 70475f83d40743134e7a97b97f2ada732fee4bcf Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 22:56:07 +0200 Subject: [PATCH 51/73] feat: Add a way to convert an entire playlist Closes #194 --- classes/ConvertedPlaylistArchiveStream.php | 29 ++++++++++++++++++++++ classes/PlaylistArchiveStream.php | 7 +++--- controllers/FrontController.php | 7 +++++- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 classes/ConvertedPlaylistArchiveStream.php diff --git a/classes/ConvertedPlaylistArchiveStream.php b/classes/ConvertedPlaylistArchiveStream.php new file mode 100644 index 0000000..ae16fe9 --- /dev/null +++ b/classes/ConvertedPlaylistArchiveStream.php @@ -0,0 +1,29 @@ +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/PlaylistArchiveStream.php b/classes/PlaylistArchiveStream.php index 78f716d..48d8b74 100644 --- a/classes/PlaylistArchiveStream.php +++ b/classes/PlaylistArchiveStream.php @@ -6,7 +6,6 @@ namespace Alltube; use Barracuda\ArchiveStream\ZipArchive; -use GuzzleHttp\Psr7\Stream; use Psr\Http\Message\StreamInterface; /** @@ -33,9 +32,9 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface /** * Current video being streamed to the archive. * - * @var Stream + * @var StreamInterface */ - private $curVideoStream; + protected $curVideoStream; /** * True if the archive is complete. @@ -234,7 +233,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface * * @return void */ - private function startVideoStream(Video $video) + protected function startVideoStream(Video $video) { $response = $video->getHttpResponse(); diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 1585f4f..7fc98ae 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -6,6 +6,7 @@ namespace Alltube\Controller; use Alltube\Config; +use Alltube\ConvertedPlaylistArchiveStream; use Alltube\EmptyUrlException; use Alltube\Locale; use Alltube\LocaleManager; @@ -378,7 +379,11 @@ class FrontController private function getStream(Request $request, Response $response) { if (isset($this->video->entries)) { - $stream = new PlaylistArchiveStream($this->video); + if ($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', From 1edd623c7f36f095a76c2cabfa75e292a016856d Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 23:16:27 +0200 Subject: [PATCH 52/73] fix: Enforce convert mode So it can't be used by adding "&audio=on" manually --- controllers/FrontController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 7fc98ae..a7d5c3e 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -331,7 +331,7 @@ class FrontController $this->video = new Video($url, $this->defaultFormat, $password); - if ($request->getQueryParam('audio')) { + if ($this->config->convert && $request->getQueryParam('audio')) { return $this->getAudioResponse($request, $response); } else { return $this->getVideoResponse($request, $response); @@ -379,7 +379,7 @@ class FrontController private function getStream(Request $request, Response $response) { if (isset($this->video->entries)) { - if ($request->getQueryParam('audio')) { + if ($this->config->convert && $request->getQueryParam('audio')) { $stream = new ConvertedPlaylistArchiveStream($this->video); } else { $stream = new PlaylistArchiveStream($this->video); From 5bae66865213d1e796223db9b0725862e56d5c31 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Apr 2019 23:26:11 +0200 Subject: [PATCH 53/73] test(phpunit): Fix tests that use convert mode --- tests/FrontControllerTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 30f703b..463722a 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -237,6 +237,8 @@ class FrontControllerTest extends BaseTest */ public function testVideoWithAudio() { + Config::setOptions(['convert' => true]); + $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]); } @@ -250,6 +252,8 @@ class FrontControllerTest extends BaseTest if (getenv('CI')) { $this->markTestSkipped('Travis is blacklisted by Vimeo.'); } + Config::setOptions(['convert' => true]); + // So we can test the fallback to default format $this->assertRequestIsOk('video', ['url' => 'https://vimeo.com/251997032', 'audio' => true]); } @@ -261,6 +265,8 @@ class FrontControllerTest extends BaseTest */ public function testVideoWithUnconvertedAudio() { + Config::setOptions(['convert' => true]); + $this->assertRequestIsRedirect( 'video', [ From 3b43018b2994150cf8e81c77f7da66d5fd6fbe1e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 22 Apr 2019 00:05:53 +0200 Subject: [PATCH 54/73] docs: Missing docblocks --- classes/ConvertedPlaylistArchiveStream.php | 3 +++ classes/Video.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/classes/ConvertedPlaylistArchiveStream.php b/classes/ConvertedPlaylistArchiveStream.php index ae16fe9..2eb2241 100644 --- a/classes/ConvertedPlaylistArchiveStream.php +++ b/classes/ConvertedPlaylistArchiveStream.php @@ -7,6 +7,9 @@ namespace Alltube; use Slim\Http\Stream; +/** + * Class used to create a Zip archive from converted playlists entries. + */ class ConvertedPlaylistArchiveStream extends PlaylistArchiveStream { /** diff --git a/classes/Video.php b/classes/Video.php index 079cf96..4dc64d9 100644 --- a/classes/Video.php +++ b/classes/Video.php @@ -566,6 +566,8 @@ class Video /** * Get a HTTP response containing the video. * + * @param array $headers HTTP headers of the request + * * @return Response */ public function getHttpResponse(array $headers = []) From 790677d13f16eaa3b462fe6c9d5f4f8b7dd2cec2 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 22 Apr 2019 15:01:53 +0200 Subject: [PATCH 55/73] build(composer): Dependencies update symfony/yaml, symfony/var-dumper, symfony/process, squizlabs/php_codesniffer --- composer.lock | 62 +++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/composer.lock b/composer.lock index b48ba78..a6d51da 100644 --- a/composer.lock +++ b/composer.lock @@ -1245,16 +1245,16 @@ }, { "name": "symfony/process", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "009f8dda80930e89e8344a4e310b08f9ff07dd2e" + "reference": "a9c4dfbf653023b668c282e4e02609d131f4057a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/009f8dda80930e89e8344a4e310b08f9ff07dd2e", - "reference": "009f8dda80930e89e8344a4e310b08f9ff07dd2e", + "url": "https://api.github.com/repos/symfony/process/zipball/a9c4dfbf653023b668c282e4e02609d131f4057a", + "reference": "a9c4dfbf653023b668c282e4e02609d131f4057a", "shasum": "" }, "require": { @@ -1290,11 +1290,11 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-01-16T13:27:11+00:00" + "time": "2019-04-08T16:15:54+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -3613,16 +3613,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.4.1", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "5b4333b4010625d29580eb4a41f1e53251be6baa" + "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5b4333b4010625d29580eb4a41f1e53251be6baa", - "reference": "5b4333b4010625d29580eb4a41f1e53251be6baa", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", + "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", "shasum": "" }, "require": { @@ -3660,20 +3660,20 @@ "phpcs", "standards" ], - "time": "2019-03-19T03:22:27+00:00" + "time": "2019-04-10T23:49:02+00:00" }, { "name": "symfony/console", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e" + "reference": "15a9104356436cb26e08adab97706654799d31d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e", - "reference": "98ae3cdc4bec48fe7ee24afc81dbb4a242186c9e", + "url": "https://api.github.com/repos/symfony/console/zipball/15a9104356436cb26e08adab97706654799d31d8", + "reference": "15a9104356436cb26e08adab97706654799d31d8", "shasum": "" }, "require": { @@ -3732,20 +3732,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-03-31T11:33:18+00:00" + "time": "2019-04-08T09:29:13+00:00" }, { "name": "symfony/debug", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "adbdd5d66342fb0a0bce7422ba68181842b6610d" + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/adbdd5d66342fb0a0bce7422ba68181842b6610d", - "reference": "adbdd5d66342fb0a0bce7422ba68181842b6610d", + "url": "https://api.github.com/repos/symfony/debug/zipball/681afbb26488903c5ac15e63734f1d8ac430c9b9", + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9", "shasum": "" }, "require": { @@ -3788,20 +3788,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-03-10T17:07:42+00:00" + "time": "2019-04-11T09:48:14+00:00" }, { "name": "symfony/finder", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b" + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fcdde4aa38f48190ce70d782c166f23930084f9b", - "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b", + "url": "https://api.github.com/repos/symfony/finder/zipball/61af5ce0b34b942d414fe8f1b11950d0e9a90e98", + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98", "shasum": "" }, "require": { @@ -3837,7 +3837,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-02-22T14:44:53+00:00" + "time": "2019-04-02T19:54:57+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -3900,16 +3900,16 @@ }, { "name": "symfony/var-dumper", - "version": "v3.4.24", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d34d10236300876d14291e9df85c6ef3d3bb9066" + "reference": "f0883812642a6d6583a9e2ae6aec4ba134436f40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d34d10236300876d14291e9df85c6ef3d3bb9066", - "reference": "d34d10236300876d14291e9df85c6ef3d3bb9066", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f0883812642a6d6583a9e2ae6aec4ba134436f40", + "reference": "f0883812642a6d6583a9e2ae6aec4ba134436f40", "shasum": "" }, "require": { @@ -3965,7 +3965,7 @@ "debug", "dump" ], - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-04-16T13:58:17+00:00" }, { "name": "theseer/tokenizer", From 660f322edd9ca7ed91913ec91bf36a8b759c7280 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 22 Apr 2019 15:02:34 +0200 Subject: [PATCH 56/73] build(composer): Upgrade slim/slim to 3.12.1 --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 575ff65..760c014 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", diff --git a/composer.lock b/composer.lock index a6d51da..f200ad4 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": "56a4c280b6d56ee4689731edc5e5f19a", + "content-hash": "2c8716c48daf667b91a6d951eb52df87", "packages": [ { "name": "aura/session", @@ -1008,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": { @@ -1075,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", From 1df06f7c22cecab74fd63f7964b00816a500ab7e Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Mon, 22 Apr 2019 15:20:05 +0200 Subject: [PATCH 57/73] refactor: Use route names that make more sense --- controllers/FrontController.php | 33 ++++--- index.php | 22 +++-- templates/index.tpl | 6 +- templates/{video.tpl => info.tpl} | 4 +- templates/playlist.tpl | 6 +- tests/FrontControllerTest.php | 144 +++++++++++++++--------------- 6 files changed, 117 insertions(+), 98 deletions(-) rename templates/{video.tpl => info.tpl} (97%) diff --git a/controllers/FrontController.php b/controllers/FrontController.php index a7d5c3e..87ee5e8 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -274,7 +274,7 @@ class FrontController * * @return Response HTTP response */ - private function getVideoResponse(Request $request, Response $response) + private function getInfoResponse(Request $request, Response $response) { try { $this->video->getJson(); @@ -285,7 +285,7 @@ class FrontController if (isset($this->video->entries)) { $template = 'playlist.tpl'; } else { - $template = 'video.tpl'; + $template = 'info.tpl'; } $title = _('Video download'); $description = _('Download video from ').$this->video->extractor_key; @@ -298,7 +298,7 @@ class FrontController $template, [ 'video' => $this->video, - 'class' => 'video', + 'class' => 'info', 'title' => $title, 'description' => $description, 'config' => $this->config, @@ -319,7 +319,7 @@ class FrontController * * @return Response HTTP response */ - public function video(Request $request, Response $response) + public function info(Request $request, Response $response) { $url = $request->getQueryParam('url') ?: $request->getQueryParam('v'); @@ -332,9 +332,13 @@ class FrontController $this->video = new Video($url, $this->defaultFormat, $password); if ($this->config->convert && $request->getQueryParam('audio')) { - return $this->getAudioResponse($request, $response); + // 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); + return $this->getInfoResponse($request, $response); } } else { return $response->withRedirect($this->container->get('router')->pathFor('index')); @@ -464,7 +468,7 @@ class FrontController } /** - * Get approriate HTTP response to redirect query + * Get approriate HTTP response to download query. * Depends on whether we want to stream, remux or simply redirect. * * @param Response $response PSR-7 response @@ -472,7 +476,7 @@ class FrontController * * @return Response HTTP response */ - private function getRedirectResponse(Request $request, Response $response) + private function getDownloadResponse(Request $request, Response $response) { try { $videoUrls = $this->video->getUrl(); @@ -532,7 +536,7 @@ class FrontController * * @return Response HTTP response */ - public function redirect(Request $request, Response $response) + public function download(Request $request, Response $response) { $format = $this->getFormat($request); $url = $request->getQueryParam('url'); @@ -541,14 +545,19 @@ class FrontController $this->video = new Video($url, $format, $this->sessionSegment->getFlash($url)); try { - if ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { + 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); } - return $this->getRedirectResponse($request, $response); + // Regular download. + return $this->getDownloadResponse($request, $response); } catch (PasswordException $e) { return $response->withRedirect( - $this->container->get('router')->pathFor('video').'?url='.urlencode($url) + $this->container->get('router')->pathFor('info').'?url='.urlencode($url) ); } catch (Exception $e) { $response->getBody()->write($e->getMessage()); diff --git a/index.php b/index.php index 77f8a85..43f7e8b 100644 --- a/index.php +++ b/index.php @@ -40,26 +40,36 @@ $app->get( '/', [$controller, 'index'] )->setName('index'); + $app->get( '/extractors', [$controller, 'extractors'] )->setName('extractors'); + $app->any( - '/video', - [$controller, 'video'] -)->setName('video'); + '/info', + [$controller, 'info'] +)->setName('info'); +// Legacy route. +$app->any('/video', [$controller, 'info']); + $app->any( '/watch', [$controller, 'video'] ); + $app->get( - '/redirect', - [$controller, 'redirect'] -)->setName('redirect'); + '/download', + [$controller, 'download'] +)->setName('download'); +// Legacy route. +$app->get('/redirect', [$controller, 'download']); + $app->get( '/json', [$controller, 'json'] )->setName('json'); + $app->get( '/locale/{locale}', [$controller, 'locale'] diff --git a/templates/index.tpl b/templates/index.tpl index 1f3c252..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} @@ -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 97% rename from templates/video.tpl rename to templates/info.tpl index b677ff0..ff977df 100644 --- a/templates/video.tpl +++ b/templates/info.tpl @@ -18,12 +18,12 @@ {/if}
- + {if isset($video->formats)}

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

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

+ {/if} {if $config->convertAdvanced} diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php index 99587ac..df89efa 100644 --- a/tests/DownloadControllerTest.php +++ b/tests/DownloadControllerTest.php @@ -67,7 +67,7 @@ class DownloadControllerTest extends ControllerTest $this->assertRequestIsOk( 'download', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'] + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'stream' => true] ); } @@ -89,6 +89,7 @@ class DownloadControllerTest extends ControllerTest [ 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', 'format' => 'hls-2176', + 'stream' => true, ] ); } From 6a126d7939538bc9bb9f71aec74f23954fb9045f Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 28 Apr 2019 14:38:55 +0200 Subject: [PATCH 73/73] build(yarn): 2.0.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5bb82aa..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": {