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 6850a247..719f8707 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 39617a0a..84134b17 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 2ab3bf07..d107cf8a 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 5ef3ef01..ae53e607 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 d107cf8a..2d9aab15 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 7c27d75d..1e22d873 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 @@ private function getConvertedAudioResponse(Request $request, Response $response,
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 @@ private function getVideoResponse(Request $request, Response $response, array $p
} 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 @@ private function getVideoResponse(Request $request, Response $response, array $p
'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 cdf4242c..4dfef1af 100644
--- a/templates/video.tpl
+++ b/templates/video.tpl
@@ -27,7 +27,7 @@
{/if}
{if $config->stream}
- webpage_url}" class="downloadBtn">Download everything
+ webpage_url}" class="downloadBtn">Download everything
{/if}
{foreach $video->entries as $entry}
{/foreach}
diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php
index 463722a2..07e1c5e6 100644
--- a/tests/FrontControllerTest.php
+++ b/tests/FrontControllerTest.php
@@ -65,12 +65,12 @@ protected function setUp()
$this->container['router']->map(['GET'], '/', [$this->controller, 'index'])
->setName('index');
- $this->container['router']->map(['GET'], '/video', [$this->controller, 'video'])
- ->setName('video');
+ $this->container['router']->map(['GET'], '/video', [$this->controller, 'info'])
+ ->setName('info');
$this->container['router']->map(['GET'], '/extractors', [$this->controller, 'extractors'])
->setName('extractors');
- $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'redirect'])
- ->setName('redirect');
+ $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'download'])
+ ->setName('download');
$this->container['router']->map(['GET'], '/locale', [$this->controller, 'locale'])
->setName('locale');
}
@@ -211,43 +211,43 @@ public function testPassword()
}
/**
- * Test the video() function without the url parameter.
+ * Test the info() function without the url parameter.
*
* @return void
*/
- public function testVideoWithoutUrl()
+ public function testInfoWithoutUrl()
{
- $this->assertRequestIsRedirect('video');
+ $this->assertRequestIsRedirect('info');
}
/**
- * Test the video() function.
+ * Test the info() function.
*
* @return void
*/
- public function testVideo()
+ public function testInfo()
{
- $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
+ $this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
}
/**
- * Test the video() function with audio conversion.
+ * Test the info() function with audio conversion.
*
* @return void
*/
- public function testVideoWithAudio()
+ public function testInfoWithAudio()
{
Config::setOptions(['convert' => true]);
- $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]);
+ $this->assertRequestIsRedirect('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]);
}
/**
- * Test the video() function with audio conversion from a Vimeo video.
+ * Test the info() function with audio conversion from a Vimeo video.
*
* @return void
*/
- public function testVideoWithVimeoAudio()
+ public function testInfoWithVimeoAudio()
{
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
@@ -255,20 +255,20 @@ public function testVideoWithVimeoAudio()
Config::setOptions(['convert' => true]);
// So we can test the fallback to default format
- $this->assertRequestIsOk('video', ['url' => 'https://vimeo.com/251997032', 'audio' => true]);
+ $this->assertRequestIsRedirect('info', ['url' => 'https://vimeo.com/251997032', 'audio' => true]);
}
/**
- * Test the video() function with audio enabled and an URL that doesn't need to be converted.
+ * Test the info() function with audio enabled and an URL that doesn't need to be converted.
*
* @return void
*/
- public function testVideoWithUnconvertedAudio()
+ public function testInfoWithUnconvertedAudio()
{
Config::setOptions(['convert' => true]);
$this->assertRequestIsRedirect(
- 'video',
+ 'info',
[
'url' => 'https://2080.bandcamp.com/track/cygnus-x-the-orange-theme-2080-faulty-chip-cover',
'audio' => true,
@@ -277,16 +277,16 @@ public function testVideoWithUnconvertedAudio()
}
/**
- * Test the video() function with a password.
+ * Test the info() function with a password.
*
* @return void
*/
- public function testVideoWithPassword()
+ public function testInfoWithPassword()
{
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
- $result = $this->controller->video(
+ $result = $this->controller->info(
$this->request->withQueryParams(['url' => 'http://vimeo.com/68375962'])
->withParsedBody(['password' => 'youtube-dl']),
$this->response
@@ -295,44 +295,44 @@ public function testVideoWithPassword()
}
/**
- * Test the video() function with a missing password.
+ * Test the info() function with a missing password.
*
* @return void
*/
- public function testVideoWithMissingPassword()
+ public function testInfoWithMissingPassword()
{
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]);
+ $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962']);
+ $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]);
}
/**
- * Test the video() function with streams enabled.
+ * Test the info() function with streams enabled.
*
* @return void
*/
- public function testVideoWithStream()
+ public function testInfoWithStream()
{
Config::setOptions(['stream' => true]);
- $this->assertRequestIsOk('video', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
+ $this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
$this->assertRequestIsOk(
- 'video',
+ 'info',
['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]
);
}
/**
- * Test the video() function with a playlist.
+ * Test the info() function with a playlist.
*
* @return void
*/
- public function testVideoWithPlaylist()
+ public function testInfoWithPlaylist()
{
$this->assertRequestIsOk(
- 'video',
+ 'info',
['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
);
}
@@ -349,59 +349,59 @@ public function testError()
}
/**
- * Test the redirect() function without the URL parameter.
+ * Test the download() function without the URL parameter.
*
* @return void
*/
- public function testRedirectWithoutUrl()
+ public function testDownloadWithoutUrl()
{
- $this->assertRequestIsRedirect('redirect');
+ $this->assertRequestIsRedirect('download');
}
/**
- * Test the redirect() function.
+ * Test the download() function.
*
* @return void
*/
- public function testRedirect()
+ public function testDownload()
{
- $this->assertRequestIsRedirect('redirect', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
+ $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
}
/**
- * Test the redirect() function with a specific format.
+ * Test the download() function with a specific format.
*
* @return void
*/
- public function testRedirectWithFormat()
+ public function testDownloadWithFormat()
{
$this->assertRequestIsRedirect(
- 'redirect',
+ 'download',
['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst']
);
}
/**
- * Test the redirect() function with streams enabled.
+ * Test the download() function with streams enabled.
*
* @return void
*/
- public function testRedirectWithStream()
+ public function testDownloadWithStream()
{
Config::setOptions(['stream' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']
);
}
/**
- * Test the redirect() function with an M3U stream.
+ * Test the download() function with an M3U stream.
*
* @return void
*/
- public function testRedirectWithM3uStream()
+ public function testDownloadWithM3uStream()
{
if (getenv('CI')) {
$this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.');
@@ -410,7 +410,7 @@ public function testRedirectWithM3uStream()
Config::setOptions(['stream' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
[
'url' => 'https://twitter.com/verge/status/813055465324056576/video/1',
'format' => 'hls-2176',
@@ -419,33 +419,33 @@ public function testRedirectWithM3uStream()
}
/**
- * Test the redirect() function with an RTMP stream.
+ * Test the download() function with an RTMP stream.
*
* @return void
*/
- public function testRedirectWithRtmpStream()
+ public function testDownloadWithRtmpStream()
{
$this->markTestIncomplete('We need to find another RTMP video.');
Config::setOptions(['stream' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264']
);
}
/**
- * Test the redirect() function with a remuxed video.
+ * Test the download() function with a remuxed video.
*
* @return void
*/
- public function testRedirectWithRemux()
+ public function testDownloadWithRemux()
{
Config::setOptions(['remux' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
[
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
'format' => 'bestvideo+bestaudio',
@@ -454,14 +454,14 @@ public function testRedirectWithRemux()
}
/**
- * Test the redirect() function with a remuxed video but remux disabled.
+ * Test the download() function with a remuxed video but remux disabled.
*
* @return void
*/
- public function testRedirectWithRemuxDisabled()
+ public function testDownloadWithRemuxDisabled()
{
$this->assertRequestIsServerError(
- 'redirect',
+ 'download',
[
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
'format' => 'bestvideo+bestaudio',
@@ -470,69 +470,69 @@ public function testRedirectWithRemuxDisabled()
}
/**
- * Test the redirect() function with a missing password.
+ * Test the download() function with a missing password.
*
* @return void
*/
- public function testRedirectWithMissingPassword()
+ public function testDownloadWithMissingPassword()
{
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
- $this->assertRequestIsRedirect('redirect', ['url' => 'http://vimeo.com/68375962']);
+ $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']);
}
/**
- * Test the redirect() function with an error.
+ * Test the download() function with an error.
*
* @return void
*/
- public function testRedirectWithError()
+ public function testDownloadWithError()
{
- $this->assertRequestIsServerError('redirect', ['url' => 'http://example.com/foo']);
+ $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']);
}
/**
- * Test the redirect() function with an video that returns an empty URL.
+ * Test the download() function with an video that returns an empty URL.
* This can be caused by trying to redirect to a playlist.
*
* @return void
*/
- public function testRedirectWithEmptyUrl()
+ public function testDownloadWithEmptyUrl()
{
$this->assertRequestIsServerError(
- 'redirect',
+ 'download',
['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
);
}
/**
- * Test the redirect() function with a playlist stream.
+ * Test the download() function with a playlist stream.
*
* @return void
* @requires OS Linux
*/
- public function testRedirectWithPlaylist()
+ public function testDownloadWithPlaylist()
{
Config::setOptions(['stream' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
);
}
/**
- * Test the redirect() function with an advanced conversion.
+ * Test the download() function with an advanced conversion.
*
* @return void
*/
- public function testRedirectWithAdvancedConversion()
+ public function testDownloadWithAdvancedConversion()
{
Config::setOptions(['convertAdvanced' => true]);
$this->assertRequestIsOk(
- 'redirect',
+ 'download',
[
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
'format' => 'best',
From 25f33bba56c7d32a5c033b04b29205cdd7983af0 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 16:05:58 +0200
Subject: [PATCH 58/73] refactor: Split FrontController into multiple classes
Closes #177
---
controllers/BaseController.php | 112 +++++++++
controllers/DownloadController.php | 274 ++++++++++++++++++++++
controllers/FrontController.php | 358 +----------------------------
controllers/JsonController.php | 44 ++++
index.php | 37 +--
tests/BaseTest.php | 2 +-
tests/ControllerTest.php | 148 ++++++++++++
tests/DownloadControllerTest.php | 228 ++++++++++++++++++
tests/FrontControllerTest.php | 341 +--------------------------
tests/JsonControllerTest.php | 63 +++++
10 files changed, 904 insertions(+), 703 deletions(-)
create mode 100644 controllers/BaseController.php
create mode 100644 controllers/DownloadController.php
create mode 100644 controllers/JsonController.php
create mode 100644 tests/ControllerTest.php
create mode 100644 tests/DownloadControllerTest.php
create mode 100644 tests/JsonControllerTest.php
diff --git a/controllers/BaseController.php b/controllers/BaseController.php
new file mode 100644
index 00000000..5af14bf3
--- /dev/null
+++ b/controllers/BaseController.php
@@ -0,0 +1,112 @@
+config = Config::getInstance();
+ $this->container = $container;
+ $session_factory = new SessionFactory();
+ $session = $session_factory->newInstance($cookies);
+ $this->sessionSegment = $session->getSegment(self::class);
+
+ if ($this->config->stream) {
+ $this->defaultFormat = 'best';
+ }
+ }
+
+ /**
+ * Get video format from request parameters or default format if none is specified.
+ *
+ * @param Request $request PSR-7 request
+ *
+ * @return string format
+ */
+ protected function getFormat(Request $request)
+ {
+ $format = $request->getQueryParam('format');
+ if (!isset($format)) {
+ $format = $this->defaultFormat;
+ }
+
+ return $format;
+ }
+
+ /**
+ * Get the password entered for the current video.
+ *
+ * @param Request $request PSR-7 request
+ *
+ * @return string Password
+ */
+ protected function getPassword(Request $request)
+ {
+ $url = $request->getQueryParam('url');
+
+ $password = $request->getParam('password');
+ if (isset($password)) {
+ $this->sessionSegment->setFlash($url, $password);
+ } else {
+ $password = $this->sessionSegment->getFlash($url);
+ }
+
+ return $password;
+ }
+}
diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php
new file mode 100644
index 00000000..ecd7496b
--- /dev/null
+++ b/controllers/DownloadController.php
@@ -0,0 +1,274 @@
+getQueryParam('url');
+
+ if (isset($url)) {
+ $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request));
+
+ try {
+ if ($this->config->convert && $request->getQueryParam('audio')) {
+ // Audio convert.
+ return $this->getAudioResponse($request, $response);
+ } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
+ // Advance convert.
+ return $this->getConvertedResponse($request, $response);
+ }
+
+ // Regular download.
+ return $this->getDownloadResponse($request, $response);
+ } catch (PasswordException $e) {
+ return $response->withRedirect(
+ $this->container->get('router')->pathFor('info').'?'.http_build_query($request->getQueryParams())
+ );
+ } catch (Exception $e) {
+ $response->getBody()->write($e->getMessage());
+
+ return $response->withHeader('Content-Type', 'text/plain')->withStatus(500);
+ }
+ } else {
+ return $response->withRedirect($this->container->get('router')->pathFor('index'));
+ }
+ }
+
+ /**
+ * Return a converted MP3 file.
+ *
+ * @param Request $request PSR-7 request
+ * @param Response $response PSR-7 response
+ *
+ * @return Response HTTP response
+ */
+ private function getConvertedAudioResponse(Request $request, Response $response)
+ {
+ $from = $request->getQueryParam('from');
+ $to = $request->getQueryParam('to');
+
+ $response = $response->withHeader(
+ 'Content-Disposition',
+ 'attachment; filename="'.
+ $this->video->getFileNameWithExtension('mp3').'"'
+ );
+ $response = $response->withHeader('Content-Type', 'audio/mpeg');
+
+ if ($request->isGet() || $request->isPost()) {
+ try {
+ $process = $this->video->getAudioStream($from, $to);
+ } catch (Exception $e) {
+ // Fallback to default format.
+ $this->video = $this->video->withFormat($this->defaultFormat);
+ $process = $this->video->getAudioStream($from, $to);
+ }
+ $response = $response->withBody(new Stream($process));
+ }
+
+ return $response;
+ }
+
+ /**
+ * Return the MP3 file.
+ *
+ * @param Request $request PSR-7 request
+ * @param Response $response PSR-7 response
+ *
+ * @return Response HTTP response
+ */
+ private function getAudioResponse(Request $request, Response $response)
+ {
+ try {
+ // First, we try to get a MP3 file directly.
+ if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) {
+ throw new Exception('Force convert when we need to seek.');
+ }
+
+ if ($this->config->stream) {
+ $this->video = $this->video->withFormat('mp3');
+
+ return $this->getStream($request, $response);
+ } else {
+ $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]');
+
+ $urls = $this->video->getUrl();
+
+ return $response->withRedirect($urls[0]);
+ }
+ } catch (PasswordException $e) {
+ $frontController = new FrontController($this->container);
+
+ return $frontController->password($request, $response);
+ } catch (Exception $e) {
+ // If MP3 is not available, we convert it.
+ $this->video = $this->video->withFormat($this->defaultFormat);
+
+ return $this->getConvertedAudioResponse($request, $response);
+ }
+ }
+
+ /**
+ * Get a video/audio stream piped through the server.
+ *
+ * @param Response $response PSR-7 response
+ * @param Request $request PSR-7 request
+ *
+ * @return Response HTTP response
+ */
+ private function getStream(Request $request, Response $response)
+ {
+ if (isset($this->video->entries)) {
+ if ($this->config->convert && $request->getQueryParam('audio')) {
+ $stream = new ConvertedPlaylistArchiveStream($this->video);
+ } else {
+ $stream = new PlaylistArchiveStream($this->video);
+ }
+ $response = $response->withHeader('Content-Type', 'application/zip');
+ $response = $response->withHeader(
+ 'Content-Disposition',
+ 'attachment; filename="'.$this->video->title.'.zip"'
+ );
+
+ return $response->withBody($stream);
+ } elseif ($this->video->protocol == 'rtmp') {
+ $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext);
+ $body = new Stream($this->video->getRtmpStream());
+ } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') {
+ $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext);
+ $body = new Stream($this->video->getM3uStream());
+ } else {
+ $stream = $this->video->getHttpResponse(['Range' => $request->getHeader('Range')]);
+
+ $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
+ $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
+ $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges'));
+ $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range'));
+ if ($stream->getStatusCode() == 206) {
+ $response = $response->withStatus(206);
+ }
+ $body = $stream->getBody();
+ }
+ if ($request->isGet()) {
+ $response = $response->withBody($body);
+ }
+ $response = $response->withHeader(
+ 'Content-Disposition',
+ 'attachment; filename="'.
+ $this->video->getFilename().'"'
+ );
+
+ return $response;
+ }
+
+ /**
+ * Get a remuxed stream piped through the server.
+ *
+ * @param Response $response PSR-7 response
+ * @param Request $request PSR-7 request
+ *
+ * @return Response HTTP response
+ */
+ private function getRemuxStream(Request $request, Response $response)
+ {
+ if (!$this->config->remux) {
+ throw new Exception(_('You need to enable remux mode to merge two formats.'));
+ }
+ $stream = $this->video->getRemuxStream();
+ $response = $response->withHeader('Content-Type', 'video/x-matroska');
+ if ($request->isGet()) {
+ $response = $response->withBody(new Stream($stream));
+ }
+
+ return $response->withHeader(
+ 'Content-Disposition',
+ 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv')
+ );
+ }
+
+ /**
+ * Get approriate HTTP response to download query.
+ * Depends on whether we want to stream, remux or simply redirect.
+ *
+ * @param Response $response PSR-7 response
+ * @param Request $request PSR-7 request
+ *
+ * @return Response HTTP response
+ */
+ private function getDownloadResponse(Request $request, Response $response)
+ {
+ try {
+ $videoUrls = $this->video->getUrl();
+ } catch (EmptyUrlException $e) {
+ /*
+ If this happens it is probably a playlist
+ so it will either be handled by getStream() or throw an exception anyway.
+ */
+ $videoUrls = [];
+ }
+ if (count($videoUrls) > 1) {
+ return $this->getRemuxStream($request, $response);
+ } elseif ($this->config->stream) {
+ return $this->getStream($request, $response);
+ } else {
+ if (empty($videoUrls[0])) {
+ throw new Exception(_("Can't find URL of video."));
+ }
+
+ return $response->withRedirect($videoUrls[0]);
+ }
+ }
+
+ /**
+ * Return a converted video file.
+ *
+ * @param Request $request PSR-7 request
+ * @param Response $response PSR-7 response
+ *
+ * @return Response HTTP response
+ */
+ private function getConvertedResponse(Request $request, Response $response)
+ {
+ $response = $response->withHeader(
+ 'Content-Disposition',
+ 'attachment; filename="'.
+ $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"'
+ );
+ $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat'));
+
+ if ($request->isGet() || $request->isPost()) {
+ $process = $this->video->getConvertedStream(
+ $request->getQueryParam('customBitrate'),
+ $request->getQueryParam('customFormat')
+ );
+ $response = $response->withBody(new Stream($process));
+ }
+
+ return $response;
+ }
+}
diff --git a/controllers/FrontController.php b/controllers/FrontController.php
index 87ee5e88..7a46b7f6 100644
--- a/controllers/FrontController.php
+++ b/controllers/FrontController.php
@@ -6,15 +6,11 @@
namespace Alltube\Controller;
use Alltube\Config;
-use Alltube\ConvertedPlaylistArchiveStream;
use Alltube\EmptyUrlException;
use Alltube\Locale;
use Alltube\LocaleManager;
use Alltube\PasswordException;
-use Alltube\PlaylistArchiveStream;
use Alltube\Video;
-use Aura\Session\Segment;
-use Aura\Session\SessionFactory;
use Exception;
use Psr\Container\ContainerInterface;
use Slim\Container;
@@ -26,36 +22,8 @@
/**
* Main controller.
*/
-class FrontController
+class FrontController extends BaseController
{
- /**
- * Config instance.
- *
- * @var Config
- */
- private $config;
-
- /**
- * Current video.
- *
- * @var Video
- */
- private $video;
-
- /**
- * Slim dependency container.
- *
- * @var ContainerInterface
- */
- private $container;
-
- /**
- * Session segment used to store session variables.
- *
- * @var Segment
- */
- private $sessionSegment;
-
/**
* Smarty view.
*
@@ -63,13 +31,6 @@ class FrontController
*/
private $view;
- /**
- * Default youtube-dl format.
- *
- * @var string
- */
- private $defaultFormat = 'best[protocol=https]/best[protocol=http]';
-
/**
* LocaleManager instance.
*
@@ -78,23 +39,17 @@ class FrontController
private $localeManager;
/**
- * FrontController constructor.
+ * BaseController constructor.
*
* @param ContainerInterface $container Slim dependency container
* @param array $cookies Cookie array
*/
public function __construct(ContainerInterface $container, array $cookies = [])
{
- $this->config = Config::getInstance();
- $this->container = $container;
- $this->view = $this->container->get('view');
+ parent::__construct($container, $cookies);
+
$this->localeManager = $this->container->get('locale');
- $session_factory = new SessionFactory();
- $session = $session_factory->newInstance($cookies);
- $this->sessionSegment = $session->getSegment(self::class);
- if ($this->config->stream) {
- $this->defaultFormat = 'best';
- }
+ $this->view = $this->container->get('view');
}
/**
@@ -195,77 +150,6 @@ public function password(Request $request, Response $response)
return $response;
}
- /**
- * Return a converted MP3 file.
- *
- * @param Request $request PSR-7 request
- * @param Response $response PSR-7 response
- *
- * @return Response HTTP response
- */
- private function getConvertedAudioResponse(Request $request, Response $response)
- {
- $from = $request->getQueryParam('from');
- $to = $request->getQueryParam('to');
-
- $response = $response->withHeader(
- 'Content-Disposition',
- 'attachment; filename="'.
- $this->video->getFileNameWithExtension('mp3').'"'
- );
- $response = $response->withHeader('Content-Type', 'audio/mpeg');
-
- if ($request->isGet() || $request->isPost()) {
- try {
- $process = $this->video->getAudioStream($from, $to);
- } catch (Exception $e) {
- // Fallback to default format.
- $this->video = $this->video->withFormat($this->defaultFormat);
- $process = $this->video->getAudioStream($from, $to);
- }
- $response = $response->withBody(new Stream($process));
- }
-
- return $response;
- }
-
- /**
- * Return the MP3 file.
- *
- * @param Request $request PSR-7 request
- * @param Response $response PSR-7 response
- *
- * @return Response HTTP response
- */
- private function getAudioResponse(Request $request, Response $response)
- {
- try {
- // First, we try to get a MP3 file directly.
- if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) {
- throw new Exception('Force convert when we need to seek.');
- }
-
- if ($this->config->stream) {
- $this->video = $this->video->withFormat('mp3');
-
- return $this->getStream($request, $response);
- } else {
- $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]');
-
- $urls = $this->video->getUrl();
-
- return $response->withRedirect($urls[0]);
- }
- } catch (PasswordException $e) {
- return $this->password($request, $response);
- } catch (Exception $e) {
- // If MP3 is not available, we convert it.
- $this->video = $this->video->withFormat($this->defaultFormat);
-
- return $this->getConvertedAudioResponse($request, $response);
- }
- }
-
/**
* Return the video description page.
*
@@ -324,12 +208,7 @@ public function info(Request $request, Response $response)
$url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
if (isset($url) && !empty($url)) {
- $password = $request->getParam('password');
- if (isset($password)) {
- $this->sessionSegment->setFlash($url, $password);
- }
-
- $this->video = new Video($url, $this->defaultFormat, $password);
+ $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request));
if ($this->config->convert && $request->getQueryParam('audio')) {
// We skip the info page and get directly to the download.
@@ -372,231 +251,6 @@ public function error(Request $request, Response $response, Exception $exception
return $response->withStatus(500);
}
- /**
- * Get a video/audio stream piped through the server.
- *
- * @param Response $response PSR-7 response
- * @param Request $request PSR-7 request
- *
- * @return Response HTTP response
- */
- private function getStream(Request $request, Response $response)
- {
- if (isset($this->video->entries)) {
- if ($this->config->convert && $request->getQueryParam('audio')) {
- $stream = new ConvertedPlaylistArchiveStream($this->video);
- } else {
- $stream = new PlaylistArchiveStream($this->video);
- }
- $response = $response->withHeader('Content-Type', 'application/zip');
- $response = $response->withHeader(
- 'Content-Disposition',
- 'attachment; filename="'.$this->video->title.'.zip"'
- );
-
- return $response->withBody($stream);
- } elseif ($this->video->protocol == 'rtmp') {
- $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext);
- $body = new Stream($this->video->getRtmpStream());
- } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') {
- $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext);
- $body = new Stream($this->video->getM3uStream());
- } else {
- $stream = $this->video->getHttpResponse(['Range' => $request->getHeader('Range')]);
-
- $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
- $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
- $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges'));
- $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range'));
- if ($stream->getStatusCode() == 206) {
- $response = $response->withStatus(206);
- }
- $body = $stream->getBody();
- }
- if ($request->isGet()) {
- $response = $response->withBody($body);
- }
- $response = $response->withHeader(
- 'Content-Disposition',
- 'attachment; filename="'.
- $this->video->getFilename().'"'
- );
-
- return $response;
- }
-
- /**
- * Get a remuxed stream piped through the server.
- *
- * @param Response $response PSR-7 response
- * @param Request $request PSR-7 request
- *
- * @return Response HTTP response
- */
- private function getRemuxStream(Request $request, Response $response)
- {
- if (!$this->config->remux) {
- throw new Exception(_('You need to enable remux mode to merge two formats.'));
- }
- $stream = $this->video->getRemuxStream();
- $response = $response->withHeader('Content-Type', 'video/x-matroska');
- if ($request->isGet()) {
- $response = $response->withBody(new Stream($stream));
- }
-
- return $response->withHeader(
- 'Content-Disposition',
- 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv')
- );
- }
-
- /**
- * Get video format from request parameters or default format if none is specified.
- *
- * @param Request $request PSR-7 request
- *
- * @return string format
- */
- private function getFormat(Request $request)
- {
- $format = $request->getQueryParam('format');
- if (!isset($format)) {
- $format = $this->defaultFormat;
- }
-
- return $format;
- }
-
- /**
- * Get approriate HTTP response to download query.
- * Depends on whether we want to stream, remux or simply redirect.
- *
- * @param Response $response PSR-7 response
- * @param Request $request PSR-7 request
- *
- * @return Response HTTP response
- */
- private function getDownloadResponse(Request $request, Response $response)
- {
- try {
- $videoUrls = $this->video->getUrl();
- } catch (EmptyUrlException $e) {
- /*
- If this happens it is probably a playlist
- so it will either be handled by getStream() or throw an exception anyway.
- */
- $videoUrls = [];
- }
- if (count($videoUrls) > 1) {
- return $this->getRemuxStream($request, $response);
- } elseif ($this->config->stream) {
- return $this->getStream($request, $response);
- } else {
- if (empty($videoUrls[0])) {
- throw new Exception(_("Can't find URL of video."));
- }
-
- return $response->withRedirect($videoUrls[0]);
- }
- }
-
- /**
- * Return a converted video file.
- *
- * @param Request $request PSR-7 request
- * @param Response $response PSR-7 response
- *
- * @return Response HTTP response
- */
- private function getConvertedResponse(Request $request, Response $response)
- {
- $response = $response->withHeader(
- 'Content-Disposition',
- 'attachment; filename="'.
- $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"'
- );
- $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat'));
-
- if ($request->isGet() || $request->isPost()) {
- $process = $this->video->getConvertedStream(
- $request->getQueryParam('customBitrate'),
- $request->getQueryParam('customFormat')
- );
- $response = $response->withBody(new Stream($process));
- }
-
- return $response;
- }
-
- /**
- * Redirect to video file.
- *
- * @param Request $request PSR-7 request
- * @param Response $response PSR-7 response
- *
- * @return Response HTTP response
- */
- public function download(Request $request, Response $response)
- {
- $format = $this->getFormat($request);
- $url = $request->getQueryParam('url');
-
- if (isset($url)) {
- $this->video = new Video($url, $format, $this->sessionSegment->getFlash($url));
-
- try {
- if ($this->config->convert && $request->getQueryParam('audio')) {
- // Audio convert.
- return $this->getAudioResponse($request, $response);
- } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
- // Advance convert.
- return $this->getConvertedResponse($request, $response);
- }
-
- // Regular download.
- return $this->getDownloadResponse($request, $response);
- } catch (PasswordException $e) {
- return $response->withRedirect(
- $this->container->get('router')->pathFor('info').'?url='.urlencode($url)
- );
- } catch (Exception $e) {
- $response->getBody()->write($e->getMessage());
-
- return $response->withHeader('Content-Type', 'text/plain')->withStatus(500);
- }
- } else {
- return $response->withRedirect($this->container->get('router')->pathFor('index'));
- }
- }
-
- /**
- * Return the JSON object generated by youtube-dl.
- *
- * @param Request $request PSR-7 request
- * @param Response $response PSR-7 response
- *
- * @return Response HTTP response
- */
- public function json(Request $request, Response $response)
- {
- $format = $this->getFormat($request);
- $url = $request->getQueryParam('url');
-
- if (isset($url)) {
- try {
- $this->video = new Video($url, $format);
-
- return $response->withJson($this->video->getJson());
- } catch (Exception $e) {
- return $response->withJson(['error' => $e->getMessage()])
- ->withStatus(500);
- }
- } else {
- return $response->withJson(['error' => 'You need to provide the url parameter'])
- ->withStatus(400);
- }
- }
-
/**
* Generate the canonical URL of the current page.
*
diff --git a/controllers/JsonController.php b/controllers/JsonController.php
new file mode 100644
index 00000000..cc36e461
--- /dev/null
+++ b/controllers/JsonController.php
@@ -0,0 +1,44 @@
+getQueryParam('url');
+
+ if (isset($url)) {
+ try {
+ $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request));
+
+ return $response->withJson($this->video->getJson());
+ } catch (Exception $e) {
+ return $response->withJson(['error' => $e->getMessage()])
+ ->withStatus(500);
+ }
+ } else {
+ return $response->withJson(['error' => 'You need to provide the url parameter'])
+ ->withStatus(400);
+ }
+ }
+}
diff --git a/index.php b/index.php
index 43f7e8b7..9e62d109 100644
--- a/index.php
+++ b/index.php
@@ -3,6 +3,8 @@
require_once __DIR__.'/vendor/autoload.php';
use Alltube\Config;
use Alltube\Controller\FrontController;
+use Alltube\Controller\JsonController;
+use Alltube\Controller\DownloadController;
use Alltube\LocaleManager;
use Alltube\LocaleMiddleware;
use Alltube\UglyRouter;
@@ -32,49 +34,52 @@
$container['locale'] = new LocaleManager($_COOKIE);
$app->add(new LocaleMiddleware($container));
-$controller = new FrontController($container, $_COOKIE);
+$frontController = new FrontController($container, $_COOKIE);
+$jsonController = new JsonController($container, $_COOKIE);
+$downloadController = new DownloadController($container, $_COOKIE);
-$container['errorHandler'] = [$controller, 'error'];
+$container['errorHandler'] = [$jsonController, 'error'];
$app->get(
'/',
- [$controller, 'index']
+ [$frontController, 'index']
)->setName('index');
$app->get(
'/extractors',
- [$controller, 'extractors']
+ [$frontController, 'extractors']
)->setName('extractors');
$app->any(
'/info',
- [$controller, 'info']
+ [$frontController, 'info']
)->setName('info');
// Legacy route.
-$app->any('/video', [$controller, 'info']);
+$app->any('/video', [$frontController, 'info']);
$app->any(
'/watch',
- [$controller, 'video']
+ [$frontController, 'video']
);
-$app->get(
+$app->any(
'/download',
- [$controller, 'download']
+ [$downloadController, 'download']
)->setName('download');
// Legacy route.
-$app->get('/redirect', [$controller, 'download']);
-
-$app->get(
- '/json',
- [$controller, 'json']
-)->setName('json');
+$app->get('/redirect', [$downloadController, 'download']);
$app->get(
'/locale/{locale}',
- [$controller, 'locale']
+ [$frontController, 'locale']
)->setName('locale');
+
+$app->get(
+ '/json',
+ [$jsonController, 'json']
+)->setName('json');
+
try {
$app->run();
} catch (SmartyException $e) {
diff --git a/tests/BaseTest.php b/tests/BaseTest.php
index d3f77969..54991013 100644
--- a/tests/BaseTest.php
+++ b/tests/BaseTest.php
@@ -9,7 +9,7 @@
use PHPUnit\Framework\TestCase;
/**
- * Unit tests for the ViewFactory class.
+ * Abstract class used by every test.
*/
abstract class BaseTest extends TestCase
{
diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php
new file mode 100644
index 00000000..d11af572
--- /dev/null
+++ b/tests/ControllerTest.php
@@ -0,0 +1,148 @@
+container = new Container();
+ $this->request = Request::createFromEnvironment(Environment::mock());
+ $this->response = new Response();
+ $this->container['view'] = ViewFactory::create($this->container, $this->request);
+ $this->container['locale'] = new LocaleManager();
+
+ $frontController = new FrontController($this->container);
+ $downloadController = new DownloadController($this->container);
+
+ $this->container['router']->map(['GET'], '/', [$frontController, 'index'])
+ ->setName('index');
+ $this->container['router']->map(['GET'], '/video', [$frontController, 'info'])
+ ->setName('info');
+ $this->container['router']->map(['GET'], '/extractors', [$frontController, 'extractors'])
+ ->setName('extractors');
+ $this->container['router']->map(['GET'], '/locale', [$frontController, 'locale'])
+ ->setName('locale');
+ $this->container['router']->map(['GET'], '/redirect', [$downloadController, 'download'])
+ ->setName('download');
+ }
+
+ /**
+ * Run controller function with custom query parameters and return the result.
+ *
+ * @param string $request Controller function to call
+ * @param array $params Query parameters
+ *
+ * @return Response HTTP response
+ */
+ protected function getRequestResult($request, array $params)
+ {
+ return $this->controller->$request(
+ $this->request->withQueryParams($params),
+ $this->response
+ );
+ }
+
+ /**
+ * Assert that calling controller function with these parameters returns a 200 HTTP response.
+ *
+ * @param string $request Controller function to call
+ * @param array $params Query parameters
+ *
+ * @return void
+ */
+ protected function assertRequestIsOk($request, array $params = [])
+ {
+ $this->assertTrue($this->getRequestResult($request, $params)->isOk());
+ }
+
+ /**
+ * Assert that calling controller function with these parameters returns an HTTP redirect.
+ *
+ * @param string $request Controller function to call
+ * @param array $params Query parameters
+ *
+ * @return void
+ */
+ protected function assertRequestIsRedirect($request, array $params = [])
+ {
+ $this->assertTrue($this->getRequestResult($request, $params)->isRedirect());
+ }
+
+ /**
+ * Assert that calling controller function with these parameters returns an HTTP 500 error.
+ *
+ * @param string $request Controller function to call
+ * @param array $params Query parameters
+ *
+ * @return void
+ */
+ protected function assertRequestIsServerError($request, array $params = [])
+ {
+ $this->assertTrue($this->getRequestResult($request, $params)->isServerError());
+ }
+
+ /**
+ * Assert that calling controller function with these parameters returns an HTTP 400 error.
+ *
+ * @param string $request Controller function to call
+ * @param array $params Query parameters
+ *
+ * @return void
+ */
+ protected function assertRequestIsClientError($request, array $params = [])
+ {
+ $this->assertTrue($this->getRequestResult($request, $params)->isClientError());
+ }
+}
diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php
new file mode 100644
index 00000000..1e1bb930
--- /dev/null
+++ b/tests/DownloadControllerTest.php
@@ -0,0 +1,228 @@
+controller = new DownloadController($this->container);
+ }
+
+ /**
+ * Test the download() function without the URL parameter.
+ *
+ * @return void
+ */
+ public function testDownloadWithoutUrl()
+ {
+ $this->assertRequestIsRedirect('download');
+ }
+
+ /**
+ * Test the download() function.
+ *
+ * @return void
+ */
+ public function testDownload()
+ {
+ $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
+ }
+
+ /**
+ * Test the download() function with a specific format.
+ *
+ * @return void
+ */
+ public function testDownloadWithFormat()
+ {
+ $this->assertRequestIsRedirect(
+ 'download',
+ ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst']
+ );
+ }
+
+ /**
+ * Test the download() function with streams enabled.
+ *
+ * @return void
+ */
+ public function testDownloadWithStream()
+ {
+ Config::setOptions(['stream' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']
+ );
+ }
+
+ /**
+ * Test the download() function with an M3U stream.
+ *
+ * @return void
+ */
+ public function testDownloadWithM3uStream()
+ {
+ if (getenv('CI')) {
+ $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.');
+ }
+
+ Config::setOptions(['stream' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ [
+ 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1',
+ 'format' => 'hls-2176',
+ ]
+ );
+ }
+
+ /**
+ * Test the download() function with an RTMP stream.
+ *
+ * @return void
+ */
+ public function testDownloadWithRtmpStream()
+ {
+ $this->markTestIncomplete('We need to find another RTMP video.');
+
+ Config::setOptions(['stream' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264']
+ );
+ }
+
+ /**
+ * Test the download() function with a remuxed video.
+ *
+ * @return void
+ */
+ public function testDownloadWithRemux()
+ {
+ Config::setOptions(['remux' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ [
+ 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
+ 'format' => 'bestvideo+bestaudio',
+ ]
+ );
+ }
+
+ /**
+ * Test the download() function with a remuxed video but remux disabled.
+ *
+ * @return void
+ */
+ public function testDownloadWithRemuxDisabled()
+ {
+ $this->assertRequestIsServerError(
+ 'download',
+ [
+ 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
+ 'format' => 'bestvideo+bestaudio',
+ ]
+ );
+ }
+
+ /**
+ * Test the download() function with a missing password.
+ *
+ * @return void
+ */
+ public function testDownloadWithMissingPassword()
+ {
+ if (getenv('CI')) {
+ $this->markTestSkipped('Travis is blacklisted by Vimeo.');
+ }
+ $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']);
+ }
+
+ /**
+ * Test the download() function with an error.
+ *
+ * @return void
+ */
+ public function testDownloadWithError()
+ {
+ $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']);
+ }
+
+ /**
+ * Test the download() function with an video that returns an empty URL.
+ * This can be caused by trying to redirect to a playlist.
+ *
+ * @return void
+ */
+ public function testDownloadWithEmptyUrl()
+ {
+ $this->assertRequestIsServerError(
+ 'download',
+ ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
+ );
+ }
+
+ /**
+ * Test the download() function with a playlist stream.
+ *
+ * @return void
+ * @requires OS Linux
+ */
+ public function testDownloadWithPlaylist()
+ {
+ Config::setOptions(['stream' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
+ );
+ }
+
+ /**
+ * Test the download() function with an advanced conversion.
+ *
+ * @return void
+ */
+ public function testDownloadWithAdvancedConversion()
+ {
+ Config::setOptions(['convertAdvanced' => true]);
+
+ $this->assertRequestIsOk(
+ 'download',
+ [
+ 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
+ 'format' => 'best',
+ 'customConvert' => 'on',
+ 'customBitrate' => 32,
+ 'customFormat' => 'flv',
+ ]
+ );
+ }
+}
diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php
index 07e1c5e6..edc7e582 100644
--- a/tests/FrontControllerTest.php
+++ b/tests/FrontControllerTest.php
@@ -18,35 +18,15 @@
/**
* Unit tests for the FrontController class.
*/
-class FrontControllerTest extends BaseTest
+class FrontControllerTest extends ControllerTest
{
- /**
- * Slim dependency container.
- *
- * @var Container
- */
- private $container;
-
- /**
- * Mock HTTP request.
- *
- * @var Request
- */
- private $request;
/**
- * Mock HTTP response.
- *
- * @var Response
- */
- private $response;
-
- /**
- * FrontController instance used in tests.
+ * Controller instance used in tests.
*
* @var FrontController
*/
- private $controller;
+ protected $controller;
/**
* Prepare tests.
@@ -55,92 +35,7 @@ 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();
-
$this->controller = new FrontController($this->container);
-
- $this->container['router']->map(['GET'], '/', [$this->controller, 'index'])
- ->setName('index');
- $this->container['router']->map(['GET'], '/video', [$this->controller, 'info'])
- ->setName('info');
- $this->container['router']->map(['GET'], '/extractors', [$this->controller, 'extractors'])
- ->setName('extractors');
- $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'download'])
- ->setName('download');
- $this->container['router']->map(['GET'], '/locale', [$this->controller, 'locale'])
- ->setName('locale');
- }
-
- /**
- * Run controller function with custom query parameters and return the result.
- *
- * @param string $request Controller function to call
- * @param array $params Query parameters
- *
- * @return Response HTTP response
- */
- private function getRequestResult($request, array $params)
- {
- return $this->controller->$request(
- $this->request->withQueryParams($params),
- $this->response
- );
- }
-
- /**
- * Assert that calling controller function with these parameters returns a 200 HTTP response.
- *
- * @param string $request Controller function to call
- * @param array $params Query parameters
- *
- * @return void
- */
- private function assertRequestIsOk($request, array $params = [])
- {
- $this->assertTrue($this->getRequestResult($request, $params)->isOk());
- }
-
- /**
- * Assert that calling controller function with these parameters returns an HTTP redirect.
- *
- * @param string $request Controller function to call
- * @param array $params Query parameters
- *
- * @return void
- */
- private function assertRequestIsRedirect($request, array $params = [])
- {
- $this->assertTrue($this->getRequestResult($request, $params)->isRedirect());
- }
-
- /**
- * Assert that calling controller function with these parameters returns an HTTP 500 error.
- *
- * @param string $request Controller function to call
- * @param array $params Query parameters
- *
- * @return void
- */
- private function assertRequestIsServerError($request, array $params = [])
- {
- $this->assertTrue($this->getRequestResult($request, $params)->isServerError());
- }
-
- /**
- * Assert that calling controller function with these parameters returns an HTTP 400 error.
- *
- * @param string $request Controller function to call
- * @param array $params Query parameters
- *
- * @return void
- */
- private function assertRequestIsClientError($request, array $params = [])
- {
- $this->assertTrue($this->getRequestResult($request, $params)->isClientError());
}
/**
@@ -239,7 +134,10 @@ public function testInfoWithAudio()
{
Config::setOptions(['convert' => true]);
- $this->assertRequestIsRedirect('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]);
+ $this->assertRequestIsRedirect(
+ 'info',
+ ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]
+ );
}
/**
@@ -348,231 +246,6 @@ public function testError()
$this->assertTrue($result->isServerError());
}
- /**
- * Test the download() function without the URL parameter.
- *
- * @return void
- */
- public function testDownloadWithoutUrl()
- {
- $this->assertRequestIsRedirect('download');
- }
-
- /**
- * Test the download() function.
- *
- * @return void
- */
- public function testDownload()
- {
- $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
- }
-
- /**
- * Test the download() function with a specific format.
- *
- * @return void
- */
- public function testDownloadWithFormat()
- {
- $this->assertRequestIsRedirect(
- 'download',
- ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst']
- );
- }
-
- /**
- * Test the download() function with streams enabled.
- *
- * @return void
- */
- public function testDownloadWithStream()
- {
- Config::setOptions(['stream' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']
- );
- }
-
- /**
- * Test the download() function with an M3U stream.
- *
- * @return void
- */
- public function testDownloadWithM3uStream()
- {
- if (getenv('CI')) {
- $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.');
- }
-
- Config::setOptions(['stream' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- [
- 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1',
- 'format' => 'hls-2176',
- ]
- );
- }
-
- /**
- * Test the download() function with an RTMP stream.
- *
- * @return void
- */
- public function testDownloadWithRtmpStream()
- {
- $this->markTestIncomplete('We need to find another RTMP video.');
-
- Config::setOptions(['stream' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264']
- );
- }
-
- /**
- * Test the download() function with a remuxed video.
- *
- * @return void
- */
- public function testDownloadWithRemux()
- {
- Config::setOptions(['remux' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- [
- 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
- 'format' => 'bestvideo+bestaudio',
- ]
- );
- }
-
- /**
- * Test the download() function with a remuxed video but remux disabled.
- *
- * @return void
- */
- public function testDownloadWithRemuxDisabled()
- {
- $this->assertRequestIsServerError(
- 'download',
- [
- 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
- 'format' => 'bestvideo+bestaudio',
- ]
- );
- }
-
- /**
- * Test the download() function with a missing password.
- *
- * @return void
- */
- public function testDownloadWithMissingPassword()
- {
- if (getenv('CI')) {
- $this->markTestSkipped('Travis is blacklisted by Vimeo.');
- }
- $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']);
- }
-
- /**
- * Test the download() function with an error.
- *
- * @return void
- */
- public function testDownloadWithError()
- {
- $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']);
- }
-
- /**
- * Test the download() function with an video that returns an empty URL.
- * This can be caused by trying to redirect to a playlist.
- *
- * @return void
- */
- public function testDownloadWithEmptyUrl()
- {
- $this->assertRequestIsServerError(
- 'download',
- ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
- );
- }
-
- /**
- * Test the download() function with a playlist stream.
- *
- * @return void
- * @requires OS Linux
- */
- public function testDownloadWithPlaylist()
- {
- Config::setOptions(['stream' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
- );
- }
-
- /**
- * Test the download() function with an advanced conversion.
- *
- * @return void
- */
- public function testDownloadWithAdvancedConversion()
- {
- Config::setOptions(['convertAdvanced' => true]);
-
- $this->assertRequestIsOk(
- 'download',
- [
- 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
- 'format' => 'best',
- 'customConvert' => 'on',
- 'customBitrate' => 32,
- 'customFormat' => 'flv',
- ]
- );
- }
-
- /**
- * Test the json() function without the URL parameter.
- *
- * @return void
- */
- public function testJsonWithoutUrl()
- {
- $this->assertRequestIsClientError('json');
- }
-
- /**
- * Test the json() function.
- *
- * @return void
- */
- public function testJson()
- {
- $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
- }
-
- /**
- * Test the json() function with an error.
- *
- * @return void
- */
- public function testJsonWithError()
- {
- $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']);
- }
-
/**
* Test the locale() function.
*
diff --git a/tests/JsonControllerTest.php b/tests/JsonControllerTest.php
new file mode 100644
index 00000000..05003f5d
--- /dev/null
+++ b/tests/JsonControllerTest.php
@@ -0,0 +1,63 @@
+controller = new JsonController($this->container);
+ }
+
+ /**
+ * Test the json() function.
+ *
+ * @return void
+ */
+ public function testJson()
+ {
+ $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
+ }
+
+ /**
+ * Test the json() function with an error.
+ *
+ * @return void
+ */
+ public function testJsonWithError()
+ {
+ $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']);
+ }
+
+ /**
+ * Test the json() function without the URL parameter.
+ *
+ * @return void
+ */
+ public function testJsonWithoutUrl()
+ {
+ $this->assertRequestIsClientError('json');
+ }
+}
From 1387d836dc28af763d55b5f9e8ff47c665bc4eb2 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 17:00:51 +0200
Subject: [PATCH 59/73] refactor: New Session class
So that session is shared between classes and does not get overwritten
---
classes/LocaleManager.php | 8 ++-----
classes/SessionManager.php | 37 +++++++++++++++++++++++++++++++++
controllers/BaseController.php | 8 +++----
controllers/FrontController.php | 5 ++---
index.php | 8 +++----
5 files changed, 48 insertions(+), 18 deletions(-)
create mode 100644 classes/SessionManager.php
diff --git a/classes/LocaleManager.php b/classes/LocaleManager.php
index af714bad..c2bbc149 100644
--- a/classes/LocaleManager.php
+++ b/classes/LocaleManager.php
@@ -6,7 +6,6 @@
namespace Alltube;
use Aura\Session\Segment;
-use Aura\Session\SessionFactory;
use Symfony\Component\Process\Process;
/**
@@ -37,13 +36,10 @@ class LocaleManager
/**
* LocaleManager constructor.
- *
- * @param array $cookies Cookie array
*/
- public function __construct(array $cookies = [])
+ public function __construct()
{
- $session_factory = new SessionFactory();
- $session = $session_factory->newInstance($cookies);
+ $session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class);
$cookieLocale = $this->sessionSegment->get('locale');
if (isset($cookieLocale)) {
diff --git a/classes/SessionManager.php b/classes/SessionManager.php
new file mode 100644
index 00000000..b461fa3b
--- /dev/null
+++ b/classes/SessionManager.php
@@ -0,0 +1,37 @@
+newInstance($_COOKIE);
+ }
+ return self::$session;
+ }
+}
diff --git a/controllers/BaseController.php b/controllers/BaseController.php
index 5af14bf3..a51eaa27 100644
--- a/controllers/BaseController.php
+++ b/controllers/BaseController.php
@@ -6,9 +6,9 @@
namespace Alltube\Controller;
use Alltube\Config;
+use Alltube\SessionManager;
use Alltube\Video;
use Aura\Session\Segment;
-use Aura\Session\SessionFactory;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Response;
@@ -57,14 +57,12 @@ abstract class BaseController
* BaseController constructor.
*
* @param ContainerInterface $container Slim dependency container
- * @param array $cookies Cookie array
*/
- public function __construct(ContainerInterface $container, array $cookies = [])
+ public function __construct(ContainerInterface $container)
{
$this->config = Config::getInstance();
$this->container = $container;
- $session_factory = new SessionFactory();
- $session = $session_factory->newInstance($cookies);
+ $session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class);
if ($this->config->stream) {
diff --git a/controllers/FrontController.php b/controllers/FrontController.php
index 7a46b7f6..c577a7da 100644
--- a/controllers/FrontController.php
+++ b/controllers/FrontController.php
@@ -42,11 +42,10 @@ class FrontController extends BaseController
* BaseController constructor.
*
* @param ContainerInterface $container Slim dependency container
- * @param array $cookies Cookie array
*/
- public function __construct(ContainerInterface $container, array $cookies = [])
+ public function __construct(ContainerInterface $container)
{
- parent::__construct($container, $cookies);
+ parent::__construct($container);
$this->localeManager = $this->container->get('locale');
$this->view = $this->container->get('view');
diff --git a/index.php b/index.php
index 9e62d109..4b46f746 100644
--- a/index.php
+++ b/index.php
@@ -31,12 +31,12 @@
if (!class_exists('Locale')) {
die('You need to install the intl extension for PHP.');
}
-$container['locale'] = new LocaleManager($_COOKIE);
+$container['locale'] = new LocaleManager();
$app->add(new LocaleMiddleware($container));
-$frontController = new FrontController($container, $_COOKIE);
-$jsonController = new JsonController($container, $_COOKIE);
-$downloadController = new DownloadController($container, $_COOKIE);
+$frontController = new FrontController($container);
+$jsonController = new JsonController($container);
+$downloadController = new DownloadController($container);
$container['errorHandler'] = [$jsonController, 'error'];
From bba2087a55e1e555ef4f5cb4faf49f743cb03018 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 17:04:59 +0200
Subject: [PATCH 60/73] style(styleci): Lint
---
classes/SessionManager.php | 2 +-
controllers/BaseController.php | 1 -
controllers/FrontController.php | 2 --
index.php | 3 +--
tests/ControllerTest.php | 2 --
tests/DownloadControllerTest.php | 8 --------
tests/FrontControllerTest.php | 5 -----
tests/JsonControllerTest.php | 9 ---------
8 files changed, 2 insertions(+), 30 deletions(-)
diff --git a/classes/SessionManager.php b/classes/SessionManager.php
index b461fa3b..0ecd586d 100644
--- a/classes/SessionManager.php
+++ b/classes/SessionManager.php
@@ -13,7 +13,6 @@
*/
class SessionManager
{
-
/**
* Current session.
*
@@ -32,6 +31,7 @@ public static function getSession()
$session_factory = new SessionFactory();
self::$session = $session_factory->newInstance($_COOKIE);
}
+
return self::$session;
}
}
diff --git a/controllers/BaseController.php b/controllers/BaseController.php
index a51eaa27..f96842e1 100644
--- a/controllers/BaseController.php
+++ b/controllers/BaseController.php
@@ -11,7 +11,6 @@
use Aura\Session\Segment;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
-use Slim\Http\Response;
/**
* Abstract class used by every controller.
diff --git a/controllers/FrontController.php b/controllers/FrontController.php
index c577a7da..51b0d8cf 100644
--- a/controllers/FrontController.php
+++ b/controllers/FrontController.php
@@ -6,7 +6,6 @@
namespace Alltube\Controller;
use Alltube\Config;
-use Alltube\EmptyUrlException;
use Alltube\Locale;
use Alltube\LocaleManager;
use Alltube\PasswordException;
@@ -16,7 +15,6 @@
use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;
-use Slim\Http\Stream;
use Slim\Views\Smarty;
/**
diff --git a/index.php b/index.php
index 4b46f746..78ee2dd7 100644
--- a/index.php
+++ b/index.php
@@ -2,9 +2,9 @@
require_once __DIR__.'/vendor/autoload.php';
use Alltube\Config;
+use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController;
use Alltube\Controller\JsonController;
-use Alltube\Controller\DownloadController;
use Alltube\LocaleManager;
use Alltube\LocaleMiddleware;
use Alltube\UglyRouter;
@@ -74,7 +74,6 @@
[$frontController, 'locale']
)->setName('locale');
-
$app->get(
'/json',
[$jsonController, 'json']
diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php
index d11af572..bad16a5a 100644
--- a/tests/ControllerTest.php
+++ b/tests/ControllerTest.php
@@ -5,13 +5,11 @@
namespace Alltube\Test;
-use Alltube\Config;
use Alltube\Controller\BaseController;
use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController;
use Alltube\LocaleManager;
use Alltube\ViewFactory;
-use Exception;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php
index 1e1bb930..99587acd 100644
--- a/tests/DownloadControllerTest.php
+++ b/tests/DownloadControllerTest.php
@@ -7,20 +7,12 @@
use Alltube\Config;
use Alltube\Controller\DownloadController;
-use Alltube\LocaleManager;
-use Alltube\ViewFactory;
-use Exception;
-use Slim\Container;
-use Slim\Http\Environment;
-use Slim\Http\Request;
-use Slim\Http\Response;
/**
* Unit tests for the FrontController class.
*/
class DownloadControllerTest extends ControllerTest
{
-
/**
* Prepare tests.
*/
diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php
index edc7e582..2c95290f 100644
--- a/tests/FrontControllerTest.php
+++ b/tests/FrontControllerTest.php
@@ -7,20 +7,15 @@
use Alltube\Config;
use Alltube\Controller\FrontController;
-use Alltube\LocaleManager;
-use Alltube\ViewFactory;
use Exception;
-use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
-use Slim\Http\Response;
/**
* Unit tests for the FrontController class.
*/
class FrontControllerTest extends ControllerTest
{
-
/**
* Controller instance used in tests.
*
diff --git a/tests/JsonControllerTest.php b/tests/JsonControllerTest.php
index 05003f5d..caefccb6 100644
--- a/tests/JsonControllerTest.php
+++ b/tests/JsonControllerTest.php
@@ -5,22 +5,13 @@
namespace Alltube\Test;
-use Alltube\Config;
use Alltube\Controller\JsonController;
-use Alltube\LocaleManager;
-use Alltube\ViewFactory;
-use Exception;
-use Slim\Container;
-use Slim\Http\Environment;
-use Slim\Http\Request;
-use Slim\Http\Response;
/**
* Unit tests for the FrontController class.
*/
class JsonControllerTest extends ControllerTest
{
-
/**
* Prepare tests.
*/
From 1a6ff90eac91fd4d3753e19214b93253e476e7a9 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 20:20:04 +0200
Subject: [PATCH 61/73] feat: Split Youtube downloads in smaller chunks
Fixes #217
---
classes/Video.php | 18 ++-
classes/YoutubeChunkStream.php | 196 +++++++++++++++++++++++++++++
classes/YoutubeStream.php | 38 ++++++
controllers/DownloadController.php | 9 +-
4 files changed, 256 insertions(+), 5 deletions(-)
create mode 100644 classes/YoutubeChunkStream.php
create mode 100644 classes/YoutubeStream.php
diff --git a/classes/Video.php b/classes/Video.php
index 4dc64d90..0e179c9e 100644
--- a/classes/Video.php
+++ b/classes/Video.php
@@ -24,6 +24,7 @@
* @property-read array $entries List of videos (if the object contains information about a playlist)
* @property-read array $rtmp_conn
* @property-read string|null $_type Object type (usually "playlist" or null)
+ * @property-read stdClass $downloader_options
*/
class Video
{
@@ -62,6 +63,12 @@ class Video
*/
private $json;
+ /**
+ * URLs of the video files.
+ * @var array
+ */
+ private $urls;
+
/**
* VideoDownload constructor.
*
@@ -219,13 +226,16 @@ public function __isset($name)
* */
public function getUrl()
{
- $urls = explode("\n", $this->getProp('get-url'));
+ // Cache the URLs.
+ if (!isset($this->urls)) {
+ $this->urls = explode("\n", $this->getProp('get-url'));
- if (empty($urls[0])) {
- throw new EmptyUrlException(_('youtube-dl returned an empty URL.'));
+ if (empty($urls[0])) {
+ throw new EmptyUrlException(_('youtube-dl returned an empty URL.'));
+ }
}
- return $urls;
+ return $this->urls;
}
/**
diff --git a/classes/YoutubeChunkStream.php b/classes/YoutubeChunkStream.php
new file mode 100644
index 00000000..3e77674e
--- /dev/null
+++ b/classes/YoutubeChunkStream.php
@@ -0,0 +1,196 @@
+response = $response;
+ }
+
+ /**
+ * Read data from the stream.
+ *
+ * @param int $length Read up to $length bytes from the object and return
+ *
+ * @return string
+ */
+ public function read($length)
+ {
+ $size = $this->response->getHeader('Content-Length')[0];
+ if ($size - $this->tell() < $length) {
+ // Don't try to read further than the end of the stream.
+ $length = $size - $this->tell();
+ }
+
+ return $this->response->getBody()->read($length);
+ }
+
+ /**
+ * Reads all data from the stream into a string, from the beginning to end.
+ */
+ public function __toString()
+ {
+ return (string) $this->response->getBody();
+ }
+
+ /**
+ * Closes the stream and any underlying resources.
+ *
+ * @return mixed
+ */
+ public function close()
+ {
+ return $this->response->getBody()->close();
+ }
+
+ /**
+ * Separates any underlying resources from the stream.
+ *
+ * @return resource|null
+ */
+ public function detach()
+ {
+ return $this->response->getBody()->detach();
+ }
+
+ /**
+ * Get the size of the stream if known.
+ *
+ * @return int|null
+ */
+ public function getSize()
+ {
+ return $this->response->getBody()->getSize();
+ }
+
+ /**
+ * Returns the current position of the file read/write pointer.
+ *
+ * @return int
+ */
+ public function tell()
+ {
+ return $this->response->getBody()->tell();
+ }
+
+ /**
+ * Returns true if the stream is at the end of the stream.
+ *
+ * @return bool
+ */
+ public function eof()
+ {
+ return $this->response->getBody()->eof();
+ }
+
+ /**
+ * Returns whether or not the stream is seekable.
+ *
+ * @return bool
+ */
+ public function isSeekable()
+ {
+ return $this->response->getBody()->isSeekable();
+ }
+
+ /**
+ * Seek to a position in the stream.
+ *
+ * @param int $offset Stream offset
+ * @param int $whence Specifies how the cursor position will be calculated
+ *
+ * @return mixed
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ return $this->response->getBody()->seek($offset, $whence);
+ }
+
+ /**
+ * Seek to the beginning of the stream.
+ *
+ * @return mixed
+ */
+ public function rewind()
+ {
+ return $this->response->getBody()->rewind();
+ }
+
+ /**
+ * Returns whether or not the stream is writable.
+ *
+ * @return bool
+ */
+ public function isWritable()
+ {
+ return $this->response->getBody()->isWritable();
+ }
+
+ /**
+ * Write data to the stream.
+ *
+ * @param string $string The string that is to be written
+ *
+ * @return mixed
+ */
+ public function write($string)
+ {
+ return $this->response->getBody()->write($string);
+ }
+
+ /**
+ * Returns whether or not the stream is readable.
+ *
+ * @return bool
+ */
+ public function isReadable()
+ {
+ return $this->response->getBody()->isReadable();
+ }
+
+ /**
+ * Returns the remaining contents in a string
+ *
+ * @return string
+ */
+ public function getContents()
+ {
+ return $this->response->getBody()->getContents();
+ }
+
+ /**
+ * Get stream metadata as an associative array or retrieve a specific key.
+ *
+ * @param string $key Specific metadata to retrieve.
+ *
+ * @return array|mixed|null
+ */
+ public function getMetadata($key = null)
+ {
+ return $this->response->getBody()->getMetadata($key);
+ }
+}
diff --git a/classes/YoutubeStream.php b/classes/YoutubeStream.php
new file mode 100644
index 00000000..d6c599ba
--- /dev/null
+++ b/classes/YoutubeStream.php
@@ -0,0 +1,38 @@
+getHttpResponse();
+ $fileSize = $stream->getHeader('Content-Length');
+ $curSize = 0;
+ while ($curSize < $fileSize[0]) {
+ $newSize = $curSize + $video->downloader_options->http_chunk_size;
+ if ($newSize > $fileSize[0]) {
+ $newSize = $fileSize[0] - 1;
+ }
+ $response = $video->getHttpResponse(['Range' => 'bytes='.$curSize.'-'.$newSize]);
+ $this->addStream(new YoutubeChunkStream($response));
+ $curSize = $newSize + 1;
+ }
+ }
+}
diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php
index ecd7496b..08e55839 100644
--- a/controllers/DownloadController.php
+++ b/controllers/DownloadController.php
@@ -10,6 +10,7 @@
use Alltube\PasswordException;
use Alltube\PlaylistArchiveStream;
use Alltube\Video;
+use Alltube\YoutubeStream;
use Exception;
use Slim\Http\Request;
use Slim\Http\Response;
@@ -172,7 +173,13 @@ private function getStream(Request $request, Response $response)
if ($stream->getStatusCode() == 206) {
$response = $response->withStatus(206);
}
- $body = $stream->getBody();
+
+ if (isset($this->video->downloader_options->http_chunk_size)) {
+ // Workaround for Youtube throttling the download speed.
+ $body = new YoutubeStream($this->video);
+ } else {
+ $body = $stream->getBody();
+ }
}
if ($request->isGet()) {
$response = $response->withBody($body);
From e13404903b1858260faf6c98a447fa4d7564a7da Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:06:05 +0200
Subject: [PATCH 62/73] refactor: Move exceptions and streams to seperate
namespaces
---
classes/Video.php | 3 +++
classes/{ => exceptions}/EmptyUrlException.php | 2 +-
classes/{ => exceptions}/PasswordException.php | 2 +-
.../{ => streams}/ConvertedPlaylistArchiveStream.php | 3 ++-
classes/{ => streams}/PlaylistArchiveStream.php | 3 ++-
classes/{ => streams}/YoutubeChunkStream.php | 5 +++--
classes/{ => streams}/YoutubeStream.php | 4 +++-
composer.json | 2 ++
controllers/DownloadController.php | 10 +++++-----
controllers/FrontController.php | 2 +-
tests/PlaylistArchiveStreamTest.php | 2 +-
11 files changed, 24 insertions(+), 14 deletions(-)
rename classes/{ => exceptions}/EmptyUrlException.php (85%)
rename classes/{ => exceptions}/PasswordException.php (85%)
rename classes/{ => streams}/ConvertedPlaylistArchiveStream.php (93%)
rename classes/{ => streams}/PlaylistArchiveStream.php (99%)
rename classes/{ => streams}/YoutubeChunkStream.php (98%)
rename classes/{ => streams}/YoutubeStream.php (92%)
diff --git a/classes/Video.php b/classes/Video.php
index 0e179c9e..0be27d99 100644
--- a/classes/Video.php
+++ b/classes/Video.php
@@ -5,6 +5,8 @@
namespace Alltube;
+use Alltube\Exception\EmptyUrlException;
+use Alltube\Exception\PasswordException;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
@@ -65,6 +67,7 @@ class Video
/**
* URLs of the video files.
+ *
* @var array
*/
private $urls;
diff --git a/classes/EmptyUrlException.php b/classes/exceptions/EmptyUrlException.php
similarity index 85%
rename from classes/EmptyUrlException.php
rename to classes/exceptions/EmptyUrlException.php
index 663501cd..232559aa 100644
--- a/classes/EmptyUrlException.php
+++ b/classes/exceptions/EmptyUrlException.php
@@ -3,7 +3,7 @@
* EmptyUrlException class.
*/
-namespace Alltube;
+namespace Alltube\Exception;
use Exception;
diff --git a/classes/PasswordException.php b/classes/exceptions/PasswordException.php
similarity index 85%
rename from classes/PasswordException.php
rename to classes/exceptions/PasswordException.php
index 598ca923..256d9c86 100644
--- a/classes/PasswordException.php
+++ b/classes/exceptions/PasswordException.php
@@ -3,7 +3,7 @@
* PasswordException class.
*/
-namespace Alltube;
+namespace Alltube\Exception;
use Exception;
diff --git a/classes/ConvertedPlaylistArchiveStream.php b/classes/streams/ConvertedPlaylistArchiveStream.php
similarity index 93%
rename from classes/ConvertedPlaylistArchiveStream.php
rename to classes/streams/ConvertedPlaylistArchiveStream.php
index 2eb2241e..b3b18397 100644
--- a/classes/ConvertedPlaylistArchiveStream.php
+++ b/classes/streams/ConvertedPlaylistArchiveStream.php
@@ -3,8 +3,9 @@
* ConvertedPlaylistArchiveStream class.
*/
-namespace Alltube;
+namespace Alltube\Stream;
+use Alltube\Video;
use Slim\Http\Stream;
/**
diff --git a/classes/PlaylistArchiveStream.php b/classes/streams/PlaylistArchiveStream.php
similarity index 99%
rename from classes/PlaylistArchiveStream.php
rename to classes/streams/PlaylistArchiveStream.php
index 48d8b747..4dc5e8d4 100644
--- a/classes/PlaylistArchiveStream.php
+++ b/classes/streams/PlaylistArchiveStream.php
@@ -3,8 +3,9 @@
* PlaylistArchiveStream class.
*/
-namespace Alltube;
+namespace Alltube\Stream;
+use Alltube\Video;
use Barracuda\ArchiveStream\ZipArchive;
use Psr\Http\Message\StreamInterface;
diff --git a/classes/YoutubeChunkStream.php b/classes/streams/YoutubeChunkStream.php
similarity index 98%
rename from classes/YoutubeChunkStream.php
rename to classes/streams/YoutubeChunkStream.php
index 3e77674e..90a73bc6 100644
--- a/classes/YoutubeChunkStream.php
+++ b/classes/streams/YoutubeChunkStream.php
@@ -3,7 +3,7 @@
* YoutubeChunkStream class.
*/
-namespace Alltube;
+namespace Alltube\Stream;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\StreamInterface;
@@ -17,6 +17,7 @@ class YoutubeChunkStream implements StreamInterface
/**
* HTTP response containing the video chunk.
+ *
* @var Response
*/
private $response;
@@ -173,7 +174,7 @@ public function isReadable()
}
/**
- * Returns the remaining contents in a string
+ * Returns the remaining contents in a string.
*
* @return string
*/
diff --git a/classes/YoutubeStream.php b/classes/streams/YoutubeStream.php
similarity index 92%
rename from classes/YoutubeStream.php
rename to classes/streams/YoutubeStream.php
index d6c599ba..cdcf9cb9 100644
--- a/classes/YoutubeStream.php
+++ b/classes/streams/YoutubeStream.php
@@ -3,9 +3,11 @@
* YoutubeStream class.
*/
-namespace Alltube;
+namespace Alltube\Stream;
+use Alltube\Video;
use GuzzleHttp\Psr7\AppendStream;
+use Psr\Http\Message\StreamInterface;
/**
* Stream that downloads a video in chunks.
diff --git a/composer.json b/composer.json
index 760c0149..89c4127b 100644
--- a/composer.json
+++ b/composer.json
@@ -79,6 +79,8 @@
"autoload": {
"psr-4": {
"Alltube\\": "classes/",
+ "Alltube\\Stream\\": "classes/streams/",
+ "Alltube\\Exception\\": "classes/exceptions/",
"Alltube\\Controller\\": "controllers/",
"Alltube\\Test\\": "tests/"
}
diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php
index 08e55839..637e8a94 100644
--- a/controllers/DownloadController.php
+++ b/controllers/DownloadController.php
@@ -5,12 +5,12 @@
namespace Alltube\Controller;
-use Alltube\ConvertedPlaylistArchiveStream;
-use Alltube\EmptyUrlException;
-use Alltube\PasswordException;
-use Alltube\PlaylistArchiveStream;
+use Alltube\Stream\ConvertedPlaylistArchiveStream;
+use Alltube\Exception\EmptyUrlException;
+use Alltube\Exception\PasswordException;
+use Alltube\Stream\PlaylistArchiveStream;
use Alltube\Video;
-use Alltube\YoutubeStream;
+use Alltube\Stream\YoutubeStream;
use Exception;
use Slim\Http\Request;
use Slim\Http\Response;
diff --git a/controllers/FrontController.php b/controllers/FrontController.php
index 51b0d8cf..903ded97 100644
--- a/controllers/FrontController.php
+++ b/controllers/FrontController.php
@@ -8,7 +8,7 @@
use Alltube\Config;
use Alltube\Locale;
use Alltube\LocaleManager;
-use Alltube\PasswordException;
+use Alltube\Exception\PasswordException;
use Alltube\Video;
use Exception;
use Psr\Container\ContainerInterface;
diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php
index 63980f2b..6d1eb6d5 100644
--- a/tests/PlaylistArchiveStreamTest.php
+++ b/tests/PlaylistArchiveStreamTest.php
@@ -5,7 +5,7 @@
namespace Alltube\Test;
-use Alltube\PlaylistArchiveStream;
+use Alltube\Stream\PlaylistArchiveStream;
use Alltube\Video;
/**
From b7bcc24320a477abdb6f9c48181f862438775490 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:07:36 +0200
Subject: [PATCH 63/73] fix: Wrong variable name
---
classes/Video.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/classes/Video.php b/classes/Video.php
index 0be27d99..127102cb 100644
--- a/classes/Video.php
+++ b/classes/Video.php
@@ -233,7 +233,7 @@ public function getUrl()
if (!isset($this->urls)) {
$this->urls = explode("\n", $this->getProp('get-url'));
- if (empty($urls[0])) {
+ if (empty($this->urls[0])) {
throw new EmptyUrlException(_('youtube-dl returned an empty URL.'));
}
}
From 84e9e86d281435512c03a38662078600ded545ee Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:08:36 +0200
Subject: [PATCH 64/73] style(styleci): Lint
---
classes/streams/YoutubeChunkStream.php | 1 -
classes/streams/YoutubeStream.php | 1 -
controllers/DownloadController.php | 4 ++--
controllers/FrontController.php | 2 +-
4 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/classes/streams/YoutubeChunkStream.php b/classes/streams/YoutubeChunkStream.php
index 90a73bc6..25c3ea83 100644
--- a/classes/streams/YoutubeChunkStream.php
+++ b/classes/streams/YoutubeChunkStream.php
@@ -14,7 +14,6 @@
*/
class YoutubeChunkStream implements StreamInterface
{
-
/**
* HTTP response containing the video chunk.
*
diff --git a/classes/streams/YoutubeStream.php b/classes/streams/YoutubeStream.php
index cdcf9cb9..35571101 100644
--- a/classes/streams/YoutubeStream.php
+++ b/classes/streams/YoutubeStream.php
@@ -7,7 +7,6 @@
use Alltube\Video;
use GuzzleHttp\Psr7\AppendStream;
-use Psr\Http\Message\StreamInterface;
/**
* Stream that downloads a video in chunks.
diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php
index 637e8a94..2d03218b 100644
--- a/controllers/DownloadController.php
+++ b/controllers/DownloadController.php
@@ -5,12 +5,12 @@
namespace Alltube\Controller;
-use Alltube\Stream\ConvertedPlaylistArchiveStream;
use Alltube\Exception\EmptyUrlException;
use Alltube\Exception\PasswordException;
+use Alltube\Stream\ConvertedPlaylistArchiveStream;
use Alltube\Stream\PlaylistArchiveStream;
-use Alltube\Video;
use Alltube\Stream\YoutubeStream;
+use Alltube\Video;
use Exception;
use Slim\Http\Request;
use Slim\Http\Response;
diff --git a/controllers/FrontController.php b/controllers/FrontController.php
index 903ded97..c89cad4e 100644
--- a/controllers/FrontController.php
+++ b/controllers/FrontController.php
@@ -6,9 +6,9 @@
namespace Alltube\Controller;
use Alltube\Config;
+use Alltube\Exception\PasswordException;
use Alltube\Locale;
use Alltube\LocaleManager;
-use Alltube\Exception\PasswordException;
use Alltube\Video;
use Exception;
use Psr\Container\ContainerInterface;
From d30614668b0e073846db5471ac887133f00672ea Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:13:57 +0200
Subject: [PATCH 65/73] test(phpunit): Fix wrong @expectedException
---
tests/VideoTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/VideoTest.php b/tests/VideoTest.php
index 48bb7e6a..c726fd4d 100644
--- a/tests/VideoTest.php
+++ b/tests/VideoTest.php
@@ -71,7 +71,7 @@ public function testgetUrlWithPassword()
* Test getUrl function with a protected video and no password.
*
* @return void
- * @expectedException Alltube\PasswordException
+ * @expectedException Alltube\Exception\PasswordException
*/
public function testgetUrlWithMissingPassword()
{
From 7bf2510dd265848adb2969bba3b94da2c353c3c2 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:52:21 +0200
Subject: [PATCH 66/73] test(phpunit): Better tests for streams
---
classes/streams/PlaylistArchiveStream.php | 2 +
tests/ControllerTest.php | 3 -
tests/ConvertedPlaylistArchiveStreamTest.php | 27 +++
tests/FrontControllerTest.php | 7 -
tests/PlaylistArchiveStreamTest.php | 167 +-------------
tests/StreamTest.php | 222 +++++++++++++++++++
tests/YoutubeChunkStreamTest.php | 27 +++
tests/YoutubeStreamTest.php | 27 +++
8 files changed, 307 insertions(+), 175 deletions(-)
create mode 100644 tests/ConvertedPlaylistArchiveStreamTest.php
create mode 100644 tests/StreamTest.php
create mode 100644 tests/YoutubeChunkStreamTest.php
create mode 100644 tests/YoutubeStreamTest.php
diff --git a/classes/streams/PlaylistArchiveStream.php b/classes/streams/PlaylistArchiveStream.php
index 4dc5e8d4..7173c0b9 100644
--- a/classes/streams/PlaylistArchiveStream.php
+++ b/classes/streams/PlaylistArchiveStream.php
@@ -191,6 +191,8 @@ public function detach()
*/
public function __toString()
{
+ $this->rewind();
+
return $this->getContents();
}
diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php
index bad16a5a..d4a94f9c 100644
--- a/tests/ControllerTest.php
+++ b/tests/ControllerTest.php
@@ -5,7 +5,6 @@
namespace Alltube\Test;
-use Alltube\Controller\BaseController;
use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController;
use Alltube\LocaleManager;
@@ -43,8 +42,6 @@ abstract class ControllerTest extends BaseTest
/**
* Controller instance used in tests.
- *
- * @var BaseController
*/
protected $controller;
diff --git a/tests/ConvertedPlaylistArchiveStreamTest.php b/tests/ConvertedPlaylistArchiveStreamTest.php
new file mode 100644
index 00000000..48a7d9e6
--- /dev/null
+++ b/tests/ConvertedPlaylistArchiveStreamTest.php
@@ -0,0 +1,27 @@
+stream = new ConvertedPlaylistArchiveStream($video);
+ }
+}
diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php
index 2c95290f..1dabf40c 100644
--- a/tests/FrontControllerTest.php
+++ b/tests/FrontControllerTest.php
@@ -16,13 +16,6 @@
*/
class FrontControllerTest extends ControllerTest
{
- /**
- * Controller instance used in tests.
- *
- * @var FrontController
- */
- protected $controller;
-
/**
* Prepare tests.
*/
diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php
index 6d1eb6d5..dfb2af73 100644
--- a/tests/PlaylistArchiveStreamTest.php
+++ b/tests/PlaylistArchiveStreamTest.php
@@ -9,17 +9,10 @@
use Alltube\Video;
/**
- * Unit tests for the ViewFactory class.
+ * Unit tests for the PlaylistArchiveStream class.
*/
-class PlaylistArchiveStreamTest extends BaseTest
+class PlaylistArchiveStreamTest extends StreamTest
{
- /**
- * PlaylistArchiveStream instance.
- *
- * @var PlaylistArchiveStream
- */
- private $stream;
-
/**
* Prepare tests.
*/
@@ -31,160 +24,4 @@ protected function setUp()
$this->stream = new PlaylistArchiveStream($video);
}
-
- /**
- * Clean variables used in tests.
- *
- * @return void
- */
- protected function tearDown()
- {
- $this->stream->close();
- }
-
- /**
- * Test the write() function.
- *
- * @return void
- */
- public function testWrite()
- {
- $this->assertNull($this->stream->write('foo'));
- }
-
- /**
- * Test the tell() function.
- *
- * @return void
- */
- public function testTell()
- {
- $this->assertInternalType('int', $this->stream->tell());
- }
-
- /**
- * Test the seek() function.
- *
- * @return void
- */
- public function testSeek()
- {
- $this->stream->write('foobar');
- $this->stream->seek(3);
- $this->assertEquals(3, $this->stream->tell());
- }
-
- /**
- * Test the read() function.
- *
- * @return void
- */
- public function testRead()
- {
- $result = $this->stream->read(8192);
- $this->assertInternalType('string', $result);
- $this->assertLessThanOrEqual(8192, strlen($result));
- }
-
- /**
- * Test the eof() function.
- *
- * @return void
- */
- public function testEof()
- {
- $this->assertFalse($this->stream->eof());
- }
-
- /**
- * Test the getSize() function.
- *
- * @return void
- */
- public function testGetSize()
- {
- $this->assertNull($this->stream->getSize());
- }
-
- /**
- * Test the isSeekable() function.
- *
- * @return void
- */
- public function testIsSeekable()
- {
- $this->assertTrue($this->stream->isSeekable());
- }
-
- /**
- * Test the rewind() function.
- *
- * @return void
- */
- public function testRewind()
- {
- $this->stream->rewind();
- $this->assertEquals(0, $this->stream->tell());
- }
-
- /**
- * Test the isWritable() function.
- *
- * @return void
- */
- public function testIsWritable()
- {
- $this->assertTrue($this->stream->isWritable());
- }
-
- /**
- * Test the isReadable() function.
- *
- * @return void
- */
- public function testIsReadable()
- {
- $this->assertTrue($this->stream->isReadable());
- }
-
- /**
- * Test the getContents() function.
- *
- * @return void
- */
- public function testGetContents()
- {
- $this->assertInternalType('string', $this->stream->getContents());
- }
-
- /**
- * Test the getMetadata() function.
- *
- * @return void
- */
- public function testGetMetadata()
- {
- $this->assertInternalType('array', $this->stream->getMetadata());
- }
-
- /**
- * Test the 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/StreamTest.php b/tests/StreamTest.php
new file mode 100644
index 00000000..d74af824
--- /dev/null
+++ b/tests/StreamTest.php
@@ -0,0 +1,222 @@
+stream->close();
+ }
+
+ /**
+ * Test the write() function.
+ *
+ * @return void
+ */
+ public function testWrite()
+ {
+ if ($this->stream->isWritable()) {
+ $this->assertNull($this->stream->write('foo'));
+ } else {
+ $this->expectException(RuntimeException::class);
+ $this->stream->write('foo');
+ }
+ }
+
+ /**
+ * Test the tell() function.
+ *
+ * @return void
+ */
+ public function testTell()
+ {
+ $this->assertInternalType('int', $this->stream->tell());
+ }
+
+ /**
+ * Test the seek() function.
+ *
+ * @return void
+ */
+ public function testSeek()
+ {
+ if ($this->stream->isSeekable()) {
+ if ($this->stream->isWritable()) {
+ // We might need some data.
+ $this->stream->write('foobar');
+ }
+
+ $this->stream->seek(3);
+ $this->assertEquals(3, $this->stream->tell());
+ } else {
+ $this->expectException(RuntimeException::class);
+ $this->stream->seek(3);
+ }
+ }
+
+ /**
+ * Test the read() function.
+ *
+ * @return void
+ */
+ public function testRead()
+ {
+ $result = $this->stream->read(8192);
+ $this->assertInternalType('string', $result);
+ $this->assertLessThanOrEqual(8192, strlen($result));
+ }
+
+ /**
+ * Test the read() function.
+ *
+ * @return void
+ */
+ public function testReadEntireStream()
+ {
+ $this->markTestIncomplete('Can we test the whole logic without reading the whole stream?');
+ }
+
+ /**
+ * Test the eof() function.
+ *
+ * @return void
+ */
+ public function testEof()
+ {
+ $this->assertFalse($this->stream->eof());
+ }
+
+ /**
+ * Test the getSize() function.
+ *
+ * @return void
+ */
+ public function testGetSize()
+ {
+ $this->assertNull($this->stream->getSize());
+ }
+
+ /**
+ * Test the isSeekable() function.
+ *
+ * @return void
+ */
+ public function testIsSeekable()
+ {
+ $this->assertInternalType('boolean', $this->stream->isSeekable());
+ }
+
+ /**
+ * Test the rewind() function.
+ *
+ * @return void
+ */
+ public function testRewind()
+ {
+ if ($this->stream->isSeekable()) {
+ if ($this->stream->isWritable()) {
+ // We might need some data.
+ $this->stream->write('foobar');
+ }
+
+ $this->stream->rewind();
+ $this->assertEquals(0, $this->stream->tell());
+ } else {
+ $this->expectException(RuntimeException::class);
+ $this->stream->rewind();
+ }
+ }
+
+ /**
+ * Test the isWritable() function.
+ *
+ * @return void
+ */
+ public function testIsWritable()
+ {
+ $this->assertInternalType('boolean', $this->stream->isWritable());
+ }
+
+ /**
+ * Test the isReadable() function.
+ *
+ * @return void
+ */
+ public function testIsReadable()
+ {
+ $this->assertTrue($this->stream->isReadable());
+ }
+
+ /**
+ * Test the getContents() function.
+ *
+ * @return void
+ */
+ public function testGetContents()
+ {
+ $this->assertInternalType('string', $this->stream->getContents());
+ }
+
+ /**
+ * Test the getMetadata() function.
+ *
+ * @return void
+ */
+ public function testGetMetadata()
+ {
+ $this->assertInternalType('array', $this->stream->getMetadata());
+ }
+
+ /**
+ * Test the getMetadata() function.
+ *
+ * @return void
+ */
+ public function testGetMetadataWithKey()
+ {
+ $this->assertInternalType('string', $this->stream->getMetadata('stream_type'));
+ $this->assertInternalType('string', $this->stream->getMetadata('mode'));
+ $this->assertInternalType('boolean', $this->stream->getMetadata('seekable'));
+ $this->assertNull($this->stream->getMetadata('foo'));
+ }
+
+ /**
+ * Test the detach() function.
+ *
+ * @return void
+ */
+ public function testDetach()
+ {
+ $this->assertInternalType('resource', $this->stream->detach());
+ }
+
+ /**
+ * Test the __toString() function.
+ *
+ * @return void
+ */
+ public function testToString()
+ {
+ $this->assertInternalType('string', $this->stream->__toString());
+ $this->assertInternalType('string', (string) $this->stream);
+ }
+}
diff --git a/tests/YoutubeChunkStreamTest.php b/tests/YoutubeChunkStreamTest.php
new file mode 100644
index 00000000..e386bd2c
--- /dev/null
+++ b/tests/YoutubeChunkStreamTest.php
@@ -0,0 +1,27 @@
+stream = new YoutubeChunkStream($video->getHttpResponse());
+ }
+}
diff --git a/tests/YoutubeStreamTest.php b/tests/YoutubeStreamTest.php
new file mode 100644
index 00000000..39073897
--- /dev/null
+++ b/tests/YoutubeStreamTest.php
@@ -0,0 +1,27 @@
+stream = new YoutubeStream($video);
+ }
+}
From a1b401c1483905523f3d501595368287aefd77c7 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Mon, 22 Apr 2019 21:55:45 +0200
Subject: [PATCH 67/73] refactor: Use less confusing variable names in
YoutubeStream
---
classes/streams/YoutubeStream.php | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/classes/streams/YoutubeStream.php b/classes/streams/YoutubeStream.php
index 35571101..bb804c01 100644
--- a/classes/streams/YoutubeStream.php
+++ b/classes/streams/YoutubeStream.php
@@ -24,16 +24,17 @@ public function __construct(Video $video)
parent::__construct();
$stream = $video->getHttpResponse();
- $fileSize = $stream->getHeader('Content-Length');
+ $contentLenghtHeader = $stream->getHeader('Content-Length');
$curSize = 0;
- while ($curSize < $fileSize[0]) {
- $newSize = $curSize + $video->downloader_options->http_chunk_size;
- if ($newSize > $fileSize[0]) {
- $newSize = $fileSize[0] - 1;
+
+ while ($rangeStart < $contentLenghtHeader[0]) {
+ $rangeEnd = $rangeStart + $video->downloader_options->http_chunk_size;
+ if ($rangeEnd > $contentLenghtHeader[0]) {
+ $rangeEnd = $contentLenghtHeader[0] - 1;
}
- $response = $video->getHttpResponse(['Range' => 'bytes='.$curSize.'-'.$newSize]);
+ $response = $video->getHttpResponse(['Range' => 'bytes='.$rangeStart.'-'.$rangeEnd]);
$this->addStream(new YoutubeChunkStream($response));
- $curSize = $newSize + 1;
+ $rangeStart = $rangeEnd + 1;
}
}
}
From 8f46166a9d7a7dd98119d79bcd5aef79ec2e07b1 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Fri, 26 Apr 2019 20:34:34 +0200
Subject: [PATCH 68/73] fix: Wrong controller
---
index.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.php b/index.php
index 78ee2dd7..791649ba 100644
--- a/index.php
+++ b/index.php
@@ -38,7 +38,7 @@
$jsonController = new JsonController($container);
$downloadController = new DownloadController($container);
-$container['errorHandler'] = [$jsonController, 'error'];
+$container['errorHandler'] = [$frontController, 'error'];
$app->get(
'/',
From f952f15851e188fefb9b4b6a276a91f913801e0c Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Fri, 26 Apr 2019 20:37:27 +0200
Subject: [PATCH 69/73] build(composer): Upgrade youtube-dl to 2019.04.24
Fixes #219
---
composer.json | 6 +++---
composer.lock | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/composer.json b/composer.json
index 89c4127b..b20218c5 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.03.18",
+ "rg3/youtube-dl": "2019.04.24",
"heroku/heroku-buildpack-php": "*",
"anam/phantomjs-linux-x86-binary": "~2.1.1",
"phpstan/phpstan": "~0.9.2"
@@ -40,10 +40,10 @@
"type": "package",
"package": {
"name": "rg3/youtube-dl",
- "version": "2019.03.18",
+ "version": "2019.04.24",
"dist": {
"type": "zip",
- "url": "https://github.com/rg3/youtube-dl/archive/2019.03.18.zip"
+ "url": "https://github.com/rg3/youtube-dl/archive/2019.04.24.zip"
}
}
},
diff --git a/composer.lock b/composer.lock
index f200ad43..7caa1ab0 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": "2c8716c48daf667b91a6d951eb52df87",
+ "content-hash": "443a32df39c89bd4e58525b41a2de772",
"packages": [
{
"name": "aura/session",
@@ -3045,10 +3045,10 @@
},
{
"name": "rg3/youtube-dl",
- "version": "2019.03.18",
+ "version": "2019.04.24",
"dist": {
"type": "zip",
- "url": "https://github.com/rg3/youtube-dl/archive/2019.03.18.zip"
+ "url": "https://github.com/rg3/youtube-dl/archive/2019.04.24.zip"
},
"type": "library"
},
From 756ff3665310c51ecad363bd6dbe615f7052f864 Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Fri, 26 Apr 2019 20:38:14 +0200
Subject: [PATCH 70/73] fix: Wrong variable name
---
classes/streams/YoutubeStream.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/classes/streams/YoutubeStream.php b/classes/streams/YoutubeStream.php
index bb804c01..9c413aa7 100644
--- a/classes/streams/YoutubeStream.php
+++ b/classes/streams/YoutubeStream.php
@@ -25,7 +25,7 @@ public function __construct(Video $video)
$stream = $video->getHttpResponse();
$contentLenghtHeader = $stream->getHeader('Content-Length');
- $curSize = 0;
+ $rangeStart = 0;
while ($rangeStart < $contentLenghtHeader[0]) {
$rangeEnd = $rangeStart + $video->downloader_options->http_chunk_size;
From 9313bc2230ebdbb081e4d64c63a98185bbc4ea2d Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Fri, 26 Apr 2019 20:43:01 +0200
Subject: [PATCH 71/73] test: Fix YoutubeStreamTest
---
tests/YoutubeStreamTest.php | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/tests/YoutubeStreamTest.php b/tests/YoutubeStreamTest.php
index 39073897..7cc75808 100644
--- a/tests/YoutubeStreamTest.php
+++ b/tests/YoutubeStreamTest.php
@@ -24,4 +24,24 @@ protected function setUp()
$this->stream = new YoutubeStream($video);
}
+
+ /**
+ * Test the getMetadata() function.
+ *
+ * @return void
+ */
+ public function testGetMetadataWithKey()
+ {
+ $this->assertNull($this->stream->getMetadata('foo'));
+ }
+
+ /**
+ * Test the detach() function.
+ *
+ * @return void
+ */
+ public function testDetach()
+ {
+ $this->assertNull($this->stream->detach());
+ }
}
From b91fe78dcd8b51ecae2a115998be1aa37891ca3e Mon Sep 17 00:00:00 2001
From: Pierre Rudloff
Date: Sun, 28 Apr 2019 00:25:03 +0200
Subject: [PATCH 72/73] feat: Make streaming optional when stream mode is
enabled
Closes #218
---
controllers/DownloadController.php | 2 +-
templates/info.tpl | 5 +++++
tests/DownloadControllerTest.php | 3 ++-
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php
index 2d03218b..ddd83b32 100644
--- a/controllers/DownloadController.php
+++ b/controllers/DownloadController.php
@@ -240,7 +240,7 @@ private function getDownloadResponse(Request $request, Response $response)
}
if (count($videoUrls) > 1) {
return $this->getRemuxStream($request, $response);
- } elseif ($this->config->stream) {
+ } elseif ($this->config->stream && (isset($this->video->entries) || $request->getQueryParam('stream'))) {
return $this->getStream($request, $response);
} else {
if (empty($videoUrls[0])) {
diff --git a/templates/info.tpl b/templates/info.tpl
index ff977df1..54847ef4 100644
--- a/templates/info.tpl
+++ b/templates/info.tpl
@@ -80,6 +80,11 @@
{/foreach}
+ {if $config->stream}
+
+
+
+ {/if}
{if $config->convertAdvanced}
diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php
index 99587acd..df89efaf 100644
--- a/tests/DownloadControllerTest.php
+++ b/tests/DownloadControllerTest.php
@@ -67,7 +67,7 @@ public function testDownloadWithStream()
$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 @@ public function testDownloadWithM3uStream()
[
'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 5bb82aa8..0e1760ef 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": {