Merge branch 'release-0.7.2-beta2'

This commit is contained in:
Pierre Rudloff 2017-04-02 20:37:58 +02:00
commit 8c58c379de
20 changed files with 1145 additions and 251 deletions

View file

@ -6,6 +6,7 @@ install:
- composer install
before_install:
- npm install -g npm@3
script: vendor/bin/phpunit
after_success:
- bash <(curl -s https://codecov.io/bash)
before_deploy:

View file

@ -5,12 +5,12 @@ RUN apt-get install -y libicu-dev xz-utils git zlib1g-dev python nodejs
RUN docker-php-ext-install mbstring
RUN docker-php-ext-install intl
RUN docker-php-ext-install zip
RUN npm install -g bower grunt-cli
RUN a2enmod rewrite
RUN curl -sS https://getcomposer.org/installer | php
COPY php.ini /usr/local/etc/php/
COPY . /var/www/html/
RUN php composer.phar install --prefer-dist
RUN npm install
RUN bower --allow-root install
RUN grunt
RUN ./node_modules/.bin/bower --allow-root install
RUN ./node_modules/.bin/grunt
ENV CONVERT=1

76
FAQ.md
View file

@ -12,15 +12,15 @@ You can ususally download the video by doing *File > Save to* or *ctrl + S*.
You need to create a YAML file called `config.yml` at the root of your project.
Here are the parameters that you can set:
* youtubedl: path to your youtube-dl binary
* python: path to your python binary
* params: an array of parameters to pass to youtube-dl
* curl_params: an array of parameters to pass to curl
* convert: true to enable audio conversion
* avconv: path to your avconv or ffmpeg binary
* rtmpdump: path to your rtmpdump binary
* `youtubedl`: path to your youtube-dl binary
* `python`: path to your python binary
* `params`: an array of parameters to pass to youtube-dl
* `curl_params`: an array of parameters to pass to curl
* `convert`: true to enable audio conversion
* `avconv`: path to your avconv or ffmpeg binary
* `rtmpdump`: path to your rtmpdump binary
See [config.example.yml](config.example.yml) for default values.
See [`config.example.yml`](config.example.yml) for default values.
## How do I enable audio conversion?
@ -61,7 +61,7 @@ Some websites generate an unique video URL for each IP address. When using Alltu
There are two known workarounds:
* You can run Alltube locally on your computer.
* You can use the experimental `feature/stream` branch which streams the video through the server in order to bypass IP restrictions.
* You can enable streaming videos through the server (see below).
Please note that this can use a lot of resources on the server (which is why we won't enable it on alltubedownload.net).
## CSS and JavaScript files are missing
@ -71,3 +71,61 @@ You need to either:
* Use a [release package](https://github.com/Rudloff/alltube/releases)
* Run `npm install` (see detailed instructions in the [README](README.md#from-git))
## I get a 404 error on every page except the index
This is probably because your server does not have mod_rewrite or AllowOverride is disabled.
You can work around this by adding this to your `config.yml` file:
```yaml
uglyUrls: true
```
## How do I enable streaming videos through the server?
You need to add this to your `config.yml` file:
```yaml
stream: true
```
Note that this can use a lot of ressources on your server.
## I want to download M3U videos
You need to enable streaming (see above).
## The downloaded videos have a strange name like `videoplayback.mp4`
Alltube can rename videos automatically if you enable streaming (see above).
## I want to download a video that isn't available in my country
If the video is available in the server's country, you can download it if you enable streaming (see above).
## How do I run the Docker image?
```bash
docker run -p 8080:80 rudloff/alltube
```
## How do I run Heroku locally?
You should be able to use `heroku local` like this:
```bash
sudo APACHE_LOCK_DIR=. APACHE_PID_FILE=./pid APACHE_RUN_USER=www-data APACHE_RUN_GROUP=www-data APACHE_LOG_DIR=. heroku local
```
You might need to create some symlinks before that:
```bash
ln -s /usr/sbin/apache2 /usr/sbin/httpd
ln -s /usr/sbin/php-fpm7.0 /usr/sbin/php-fpm
```
And you probably need to run this in another terminal after `heroku local` has finished launching `php-fpm`:
```bash
chmod 0667 /tmp/heroku.fcgi.5000.sock
```

View file

@ -33,6 +33,16 @@ chmod 777 templates_c/
If your web server is Apache, you need to set the `AllowOverride` setting to `All` or `FileInfo`.
#### Update
When updating from Git, you need to run npm and Composer again:
```bash
git pull
npm install
composer install
```
## Config
If you want to use a custom config, you need to create a config file:

View file

@ -38,7 +38,7 @@ class Config
*
* @var array
*/
public $params = ['--no-playlist', '--no-warnings', '-f best[protocol^=http]', '--playlist-end', 1];
public $params = ['--no-playlist', '--no-warnings', '--playlist-end', 1];
/**
* Enable audio conversion.
@ -82,6 +82,13 @@ class Config
*/
public $uglyUrls = false;
/**
* Stream downloaded files trough server?
*
* @var bool
*/
public $stream = false;
/**
* YAML config file path.
*

View file

@ -295,4 +295,33 @@ class VideoDownload
return popen($chain->getProcess()->getCommandLine(), 'r');
}
/**
* Get video stream from an M3U playlist.
*
* @param \stdClass $video Video object returned by getJSON
*
* @return resource popen stream
*/
public function getM3uStream(\stdClass $video)
{
if (!shell_exec('which '.$this->config->avconv)) {
throw(new \Exception('Can\'t find avconv or ffmpeg'));
}
$procBuilder = ProcessBuilder::create(
[
$this->config->avconv,
'-v', 'quiet',
'-i', $video->url,
'-f', $video->ext,
'-c', 'copy',
'-bsf:a', 'aac_adtstoasc',
'-movflags', 'frag_keyframe+empty_moov',
'pipe:1',
]
);
return popen($procBuilder->getProcess()->getCommandLine(), 'r');
}
}

View file

@ -12,6 +12,8 @@
"symfony/process": "~3.2.0",
"ptachoire/process-builder-chain": "~1.2.0",
"rudloff/smarty-plugin-noscheme": "~0.1.0",
"guzzlehttp/guzzle": "~6.2.0",
"rudloff/rtmpdump-bin": "~2.3",
"aura/session": "~2.1.0"
},
"require-dev": {
@ -19,7 +21,7 @@
"squizlabs/php_codesniffer": "~2.7.0",
"phpunit/phpunit": "~5.7.2",
"ffmpeg/ffmpeg": "dev-release",
"rg3/youtube-dl": "~2017.01.10",
"rg3/youtube-dl": "~2017.03.07",
"rudloff/rtmpdump-bin": "~2.3",
"heroku/heroku-buildpack-php": "*"
},
@ -35,10 +37,10 @@
"type": "package",
"package": {
"name": "rg3/youtube-dl",
"version": "2017.01.10",
"version": "2017.03.07",
"dist": {
"type": "zip",
"url": "https://github.com/rg3/youtube-dl/archive/2017.01.10.zip"
"url": "https://github.com/rg3/youtube-dl/archive/2017.03.07.zip"
}
}
},
@ -48,7 +50,7 @@
"name": "ffmpeg/ffmpeg",
"version": "dev-release",
"dist": {
"url": "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz",
"url": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz",
"type": "xz"
},
"bin": [
@ -77,9 +79,6 @@
"Alltube\\Controller\\": "controllers/"
}
},
"config": {
"secure-http": false
},
"scripts": {
"compile": "composer install --dev"
}

495
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "16268cc5135ed42aedf75446f4e39bd7",
"content-hash": "1ac742cf7c2832a7af628c157ced2dbc",
"packages": [
{
"name": "aura/session",
@ -70,18 +70,21 @@
},
{
"name": "container-interop/container-interop",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
@ -93,7 +96,186 @@
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"time": "2014-12-30T15:22:37+00:00"
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "6.2.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006",
"reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.4",
"php": ">=5.5"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0",
"psr/log": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.2-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2017-02-28T22:50:30+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"time": "2016-12-20T10:07:11+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/0d6c7ca039329247e4f0f8f8f6506810e8248855",
"reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"request",
"response",
"stream",
"uri",
"url"
],
"time": "2017-02-27T10:51:17+00:00"
},
{
"name": "jeremykendall/php-domain-parser",
@ -273,16 +455,16 @@
},
{
"name": "nikic/fast-route",
"version": "v1.1.0",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "f3dcf5130e634b6123d40727d612ec6aa4f61fb3"
"reference": "b5f95749071c82a8e0f58586987627054400cdf6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/f3dcf5130e634b6123d40727d612ec6aa4f61fb3",
"reference": "f3dcf5130e634b6123d40727d612ec6aa4f61fb3",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/b5f95749071c82a8e0f58586987627054400cdf6",
"reference": "b5f95749071c82a8e0f58586987627054400cdf6",
"shasum": ""
},
"require": {
@ -312,7 +494,7 @@
"router",
"routing"
],
"time": "2016-10-20T17:36:47+00:00"
"time": "2017-01-19T11:35:12+00:00"
},
{
"name": "pimple/pimple",
@ -360,6 +542,55 @@
],
"time": "2015-09-11T15:10:35+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
@ -446,6 +677,34 @@
"description": "Add ability to chain symfony processes",
"time": "2016-04-10T08:33:20+00:00"
},
{
"name": "rudloff/rtmpdump-bin",
"version": "2.3",
"source": {
"type": "git",
"url": "https://github.com/Rudloff/rtmpdump-bin.git",
"reference": "133cdd80e3bab66593e88a5276158596383afd97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Rudloff/rtmpdump-bin/zipball/133cdd80e3bab66593e88a5276158596383afd97",
"reference": "133cdd80e3bab66593e88a5276158596383afd97",
"shasum": ""
},
"require-dev": {
"rtmpdump/rtmpdump": "2.3"
},
"bin": [
"rtmpdump"
],
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0"
],
"description": "rtmpdump binary for Linux 64 bit",
"time": "2016-04-12T19:17:32+00:00"
},
{
"name": "rudloff/smarty-plugin-noscheme",
"version": "0.1.1",
@ -612,16 +871,16 @@
},
{
"name": "symfony/process",
"version": "v3.2.1",
"version": "v3.2.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "02ea84847aad71be7e32056408bb19f3a616cdd3"
"reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/02ea84847aad71be7e32056408bb19f3a616cdd3",
"reference": "02ea84847aad71be7e32056408bb19f3a616cdd3",
"url": "https://api.github.com/repos/symfony/process/zipball/68bfa8c83f24c0ac04ea7193bcdcda4519f41892",
"reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892",
"shasum": ""
},
"require": {
@ -657,20 +916,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2016-11-24T10:40:28+00:00"
"time": "2017-03-04T12:23:14+00:00"
},
{
"name": "symfony/yaml",
"version": "v3.2.1",
"version": "v3.2.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "a7095af4b97a0955f85c8989106c249fa649011f"
"reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/a7095af4b97a0955f85c8989106c249fa649011f",
"reference": "a7095af4b97a0955f85c8989106c249fa649011f",
"url": "https://api.github.com/repos/symfony/yaml/zipball/093e416ad096355149e265ea2e4cc1f9ee40ab1a",
"reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a",
"shasum": ""
},
"require": {
@ -712,7 +971,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2016-12-10T10:07:06+00:00"
"time": "2017-03-07T16:47:02+00:00"
}
],
"packages-dev": [
@ -775,7 +1034,7 @@
"version": "dev-release",
"dist": {
"type": "xz",
"url": "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz",
"url": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz",
"reference": null,
"shasum": null
},
@ -786,16 +1045,16 @@
},
{
"name": "heroku/heroku-buildpack-php",
"version": "v117",
"version": "v120",
"source": {
"type": "git",
"url": "https://github.com/heroku/heroku-buildpack-php.git",
"reference": "960199a978308c75926fd9bb4775f7113bf1d777"
"reference": "e0499a7fdffd56f46534a037a6c48d65cef4e645"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/960199a978308c75926fd9bb4775f7113bf1d777",
"reference": "960199a978308c75926fd9bb4775f7113bf1d777",
"url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/e0499a7fdffd56f46534a037a6c48d65cef4e645",
"reference": "e0499a7fdffd56f46534a037a6c48d65cef4e645",
"shasum": ""
},
"bin": [
@ -826,20 +1085,20 @@
"nginx",
"php"
],
"time": "2016-12-09T19:37:38+00:00"
"time": "2017-02-20T15:05:49+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.5.5",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108"
"reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108",
"reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe",
"reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe",
"shasum": ""
},
"require": {
@ -868,7 +1127,7 @@
"object",
"object graph"
],
"time": "2016-10-31T17:19:45+00:00"
"time": "2017-01-26T22:05:40+00:00"
},
{
"name": "phpdocumentor/reflection-common",
@ -1018,27 +1277,27 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.6.2",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb"
"reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb",
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
"reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
"sebastian/comparator": "^1.1",
"sebastian/recursion-context": "^1.0|^2.0"
"sebastian/comparator": "^1.1|^2.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.0",
"phpspec/phpspec": "^2.5|^3.2",
"phpunit/phpunit": "^4.8 || ^5.6.5"
},
"type": "library",
@ -1077,39 +1336,39 @@
"spy",
"stub"
],
"time": "2016-11-21T14:58:47+00:00"
"time": "2017-03-02T20:05:34+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "4.0.4",
"version": "4.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "c14196e64a78570034afd0b7a9f3757ba71c2a0a"
"reference": "09e2277d14ea467e5a984010f501343ef29ffc69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c14196e64a78570034afd0b7a9f3757ba71c2a0a",
"reference": "c14196e64a78570034afd0b7a9f3757ba71c2a0a",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/09e2277d14ea467e5a984010f501343ef29ffc69",
"reference": "09e2277d14ea467e5a984010f501343ef29ffc69",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xmlwriter": "*",
"php": "^5.6 || ^7.0",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "^1.4.2",
"sebastian/code-unit-reverse-lookup": "~1.0",
"phpunit/php-file-iterator": "^1.3",
"phpunit/php-text-template": "^1.2",
"phpunit/php-token-stream": "^1.4.2 || ^2.0",
"sebastian/code-unit-reverse-lookup": "^1.0",
"sebastian/environment": "^1.3.2 || ^2.0",
"sebastian/version": "~1.0|~2.0"
"sebastian/version": "^1.0 || ^2.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "^5.4"
"ext-xdebug": "^2.1.4",
"phpunit/phpunit": "^5.7"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.4.0",
"ext-xmlwriter": "*"
"ext-xdebug": "^2.5.1"
},
"type": "library",
"extra": {
@ -1140,7 +1399,7 @@
"testing",
"xunit"
],
"time": "2016-12-20T15:22:42+00:00"
"time": "2017-03-01T09:12:17+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1232,25 +1491,30 @@
},
{
"name": "phpunit/php-timer",
"version": "1.0.8",
"version": "1.0.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260"
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
"reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": "^5.3.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~4|~5"
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
@ -1272,20 +1536,20 @@
"keywords": [
"timer"
],
"time": "2016-05-12T18:03:57+00:00"
"time": "2017-02-26T11:10:40+00:00"
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.9",
"version": "1.4.11",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b"
"reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b",
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
"reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
"shasum": ""
},
"require": {
@ -1321,20 +1585,20 @@
"keywords": [
"tokenizer"
],
"time": "2016-11-15T14:06:22+00:00"
"time": "2017-02-27T10:12:30+00:00"
},
{
"name": "phpunit/phpunit",
"version": "5.7.5",
"version": "5.7.16",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe"
"reference": "dafc78e2a7d12139b0e97078d1082326bd09363d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50fd2be8f3e23e91da825f36f08e5f9633076ffe",
"reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/dafc78e2a7d12139b0e97078d1082326bd09363d",
"reference": "dafc78e2a7d12139b0e97078d1082326bd09363d",
"shasum": ""
},
"require": {
@ -1346,19 +1610,19 @@
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.6.2",
"phpunit/php-code-coverage": "^4.0.3",
"phpunit/php-code-coverage": "^4.0.4",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.2",
"sebastian/comparator": "~1.2.2",
"sebastian/comparator": "^1.2.4",
"sebastian/diff": "~1.2",
"sebastian/environment": "^1.3.4 || ^2.0",
"sebastian/exporter": "~2.0",
"sebastian/global-state": "^1.0 || ^2.0",
"sebastian/global-state": "^1.1",
"sebastian/object-enumerator": "~2.0",
"sebastian/resource-operations": "~1.0",
"sebastian/version": "~1.0|~2.0",
"sebastian/version": "~1.0.3|~2.0",
"symfony/yaml": "~2.1|~3.0"
},
"conflict": {
@ -1403,7 +1667,7 @@
"testing",
"xunit"
],
"time": "2016-12-28T07:18:51+00:00"
"time": "2017-03-15T13:02:34+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -1466,62 +1730,34 @@
},
{
"name": "rg3/youtube-dl",
"version": "2017.01.10",
"version": "2017.03.07",
"dist": {
"type": "zip",
"url": "https://github.com/rg3/youtube-dl/archive/2017.01.10.zip",
"url": "https://github.com/rg3/youtube-dl/archive/2017.03.07.zip",
"reference": null,
"shasum": null
},
"type": "library"
},
{
"name": "rudloff/rtmpdump-bin",
"version": "2.3",
"source": {
"type": "git",
"url": "https://github.com/Rudloff/rtmpdump-bin.git",
"reference": "133cdd80e3bab66593e88a5276158596383afd97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Rudloff/rtmpdump-bin/zipball/133cdd80e3bab66593e88a5276158596383afd97",
"reference": "133cdd80e3bab66593e88a5276158596383afd97",
"shasum": ""
},
"require-dev": {
"rtmpdump/rtmpdump": "2.3"
},
"bin": [
"rtmpdump"
],
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0"
],
"description": "rtmpdump binary for Linux 64 bit",
"time": "2016-04-12T19:17:32+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
"reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
"reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
"shasum": ""
},
"require": {
"php": ">=5.6"
"php": "^5.6 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~5"
"phpunit/phpunit": "^5.7 || ^6.0"
},
"type": "library",
"extra": {
@ -1546,20 +1782,20 @@
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"time": "2016-02-13T06:45:14+00:00"
"time": "2017-03-04T06:30:41+00:00"
},
{
"name": "sebastian/comparator",
"version": "1.2.2",
"version": "1.2.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f"
"reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
"reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
"reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
"shasum": ""
},
"require": {
@ -1610,7 +1846,7 @@
"compare",
"equality"
],
"time": "2016-11-19T09:18:40+00:00"
"time": "2017-01-29T09:50:25+00:00"
},
{
"name": "sebastian/diff",
@ -1834,16 +2070,16 @@
},
{
"name": "sebastian/object-enumerator",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35"
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
"reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
"shasum": ""
},
"require": {
@ -1876,7 +2112,7 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2016-11-19T07:35:10+00:00"
"time": "2017-02-18T15:18:39+00:00"
},
{
"name": "sebastian/recursion-context",
@ -2155,22 +2391,25 @@
},
{
"name": "symfony/var-dumper",
"version": "v3.2.1",
"version": "v3.2.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "f722532b0966e9b6fc631e682143c07b2cf583a0"
"reference": "4100f347aff890bc16b0b4b42843b599db257b2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/f722532b0966e9b6fc631e682143c07b2cf583a0",
"reference": "f722532b0966e9b6fc631e682143c07b2cf583a0",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/4100f347aff890bc16b0b4b42843b599db257b2d",
"reference": "4100f347aff890bc16b0b4b42843b599db257b2d",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
},
"require-dev": {
"twig/twig": "~1.20|~2.0"
},
@ -2214,7 +2453,7 @@
"debug",
"dump"
],
"time": "2016-12-11T14:34:22+00:00"
"time": "2017-02-20T13:45:48+00:00"
},
{
"name": "webmozart/assert",

View file

@ -3,7 +3,6 @@ python: /usr/bin/python
params:
- --no-playlist
- --no-warnings
- -f best[protocol^=http]
- --playlist-end
- 1
curl_params:
@ -12,3 +11,4 @@ avconv: vendor/bin/ffmpeg
rtmpdump: vendor/bin/rtmpdump
curl: /usr/bin/curl
uglyUrls: false
stream: false

View file

@ -54,6 +54,13 @@ class FrontController
*/
private $view;
/**
* Default youtube-dl format.
*
* @var string
*/
private $defaultFormat = 'best[protocol^=http]';
/**
* FrontController constructor.
*
@ -68,6 +75,9 @@ class FrontController
$session_factory = new \Aura\Session\SessionFactory();
$session = $session_factory->newInstance($_COOKIE);
$this->sessionSegment = $session->getSegment('Alltube\Controller\FrontController');
if ($this->config->stream) {
$this->defaultFormat = 'best';
}
}
/**
@ -76,10 +86,11 @@ class FrontController
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
*
* @return void
* @return Response HTTP response
*/
public function index(Request $request, Response $response)
{
$uri = $request->getUri();
$this->view->render(
$response,
'index.tpl',
@ -88,8 +99,12 @@ class FrontController
'uglyUrls' => $this->config->uglyUrls,
'class' => 'index',
'description' => 'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.',
'domain' => $uri->getScheme().'://'.$uri->getAuthority(),
'canonical' => $this->getCanonicalUrl($request),
]
);
return $response;
}
/**
@ -98,7 +113,7 @@ class FrontController
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
*
* @return void
* @return Response HTTP response
*/
public function extractors(Request $request, Response $response)
{
@ -111,8 +126,11 @@ class FrontController
'title' => 'Supported websites',
'description' => 'List of all supported websites from which Alltube Download '.
'can extract video or audio files',
'canonical' => $this->getCanonicalUrl($request),
]
);
return $response;
}
/**
@ -132,8 +150,89 @@ class FrontController
'class' => 'password',
'title' => 'Password prompt',
'description' => 'You need a password in order to download this video with Alltube Download',
'canonical' => $this->getCanonicalUrl($request),
]
);
return $response;
}
/**
* Return the converted MP3 file.
*
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
* @param array $params GET query parameters
* @param string $password Video password
*
* @return Response HTTP response
*/
private function getAudioResponse(Request $request, Response $response, array $params, $password = null)
{
try {
if ($this->config->stream) {
return $this->getStream($params['url'], 'mp3', $response, $request, $password);
} else {
$url = $this->download->getURL($params['url'], 'mp3[protocol^=http]', $password);
return $response->withRedirect($url);
}
} catch (PasswordException $e) {
return $this->password($request, $response);
} catch (\Exception $e) {
$response = $response->withHeader(
'Content-Disposition',
'attachment; filename="'.
$this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"'
);
$response = $response->withHeader('Content-Type', 'audio/mpeg');
if ($request->isGet() || $request->isPost()) {
$process = $this->download->getAudioStream($params['url'], 'bestaudio/best', $password);
$response = $response->withBody(new Stream($process));
}
return $response;
}
}
/**
* Return the video description page.
*
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
* @param array $params GET query parameters
* @param string $password Video password
*
* @return Response HTTP response
*/
private function getVideoResponse(Request $request, Response $response, array $params, $password = null)
{
try {
$video = $this->download->getJSON($params['url'], $this->defaultFormat, $password);
} catch (PasswordException $e) {
return $this->password($request, $response);
}
if ($this->config->stream) {
$protocol = '';
} else {
$protocol = '[protocol^=http]';
}
$this->view->render(
$response,
'video.tpl',
[
'video' => $video,
'class' => 'video',
'title' => $video->title,
'description' => 'Download "'.$video->title.'" from '.$video->extractor_key,
'protocol' => $protocol,
'config' => $this->config,
'canonical' => $this->getCanonicalUrl($request),
]
);
return $response;
}
/**
@ -153,43 +252,9 @@ class FrontController
$this->sessionSegment->setFlash($params['url'], $password);
}
if (isset($params['audio'])) {
try {
$url = $this->download->getURL($params['url'], 'mp3[protocol^=http]', $password);
return $response->withRedirect($url);
} catch (PasswordException $e) {
return $this->password($request, $response);
} catch (\Exception $e) {
$response = $response->withHeader(
'Content-Disposition',
'attachment; filename="'.
$this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"'
);
$response = $response->withHeader('Content-Type', 'audio/mpeg');
if ($request->isGet() || $request->isPost()) {
$process = $this->download->getAudioStream($params['url'], 'bestaudio/best', $password);
$response = $response->withBody(new Stream($process));
}
return $response;
}
return $this->getAudioResponse($request, $response, $params, $password);
} else {
try {
$video = $this->download->getJSON($params['url'], null, $password);
} catch (PasswordException $e) {
return $this->password($request, $response);
}
$this->view->render(
$response,
'video.tpl',
[
'video' => $video,
'class' => 'video',
'title' => $video->title,
'description' => 'Download "'.$video->title.'" from '.$video->extractor_key,
]
);
return $this->getVideoResponse($request, $response, $params, $password);
}
} else {
return $response->withRedirect($this->container->get('router')->pathFor('index'));
@ -211,15 +276,50 @@ class FrontController
$response,
'error.tpl',
[
'errors' => $exception->getMessage(),
'class' => 'video',
'title' => 'Error',
'errors' => $exception->getMessage(),
'class' => 'video',
'title' => 'Error',
'canonical' => $this->getCanonicalUrl($request),
]
);
return $response->withStatus(500);
}
/**
* Get a video/audio stream piped through the server.
*
* @param string $url URL of the video
* @param string $format Requested format
* @param Response $response PSR-7 response
* @param Request $request PSR-7 request
* @param string $password Video password
*
* @return Response HTTP response
*/
private function getStream($url, $format, $response, $request, $password = null)
{
$video = $this->download->getJSON($url, $format, $password);
if ($video->protocol == 'm3u8') {
$stream = $this->download->getM3uStream($video);
$response = $response->withHeader('Content-Type', 'video/'.$video->ext);
if ($request->isGet()) {
$response = $response->withBody(new Stream($stream));
}
} else {
$client = new \GuzzleHttp\Client();
$stream = $client->request('GET', $video->url, ['stream' => true]);
$response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
$response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
if ($request->isGet()) {
$response = $response->withBody($stream->getBody());
}
}
$response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"');
return $response;
}
/**
* Redirect to video file.
*
@ -231,15 +331,30 @@ class FrontController
public function redirect(Request $request, Response $response)
{
$params = $request->getQueryParams();
if (isset($params['format'])) {
$format = $params['format'];
} else {
$format = $this->defaultFormat;
}
if (isset($params['url'])) {
try {
$url = $this->download->getURL(
$params['url'],
$request->getParam('format'),
$this->sessionSegment->getFlash($params['url'])
);
if ($this->config->stream) {
return $this->getStream(
$params['url'],
$format,
$response,
$request,
$this->sessionSegment->getFlash($params['url'])
);
} else {
$url = $this->download->getURL(
$params['url'],
$format,
$this->sessionSegment->getFlash($params['url'])
);
return $response->withRedirect($url);
return $response->withRedirect($url);
}
} catch (PasswordException $e) {
return $response->withRedirect(
$this->container->get('router')->pathFor('video').'?url='.urlencode($params['url'])
@ -247,32 +362,35 @@ class FrontController
} catch (\Exception $e) {
$response->getBody()->write($e->getMessage());
return $response->withHeader('Content-Type', 'text/plain');
return $response->withHeader('Content-Type', 'text/plain')->withStatus(500);
}
} else {
return $response->withRedirect($this->container->get('router')->pathFor('index'));
}
}
/**
* Output JSON info about the video.
* Generate the canonical URL of the current page.
*
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
* @param Request $request PSR-7 Request
*
* @return Response HTTP response
* @return string URL
*/
public function json(Request $request, Response $response)
private function getCanonicalUrl(Request $request)
{
$params = $request->getQueryParams();
if (isset($params['url'])) {
try {
$video = $this->download->getJSON($params['url']);
$uri = $request->getUri();
$return = 'https://alltubedownload.net/';
return $response->withJson($video);
} catch (\Exception $e) {
return $response->withJson(
['success' => false, 'error' => $e->getMessage()]
);
}
$path = $uri->getPath();
if ($path != '/') {
$return .= $path;
}
$query = $uri->getQuery();
if (!empty($query)) {
$return .= '?'.$query;
}
return $return;
}
}

View file

@ -48,8 +48,4 @@ $app->get(
'/redirect',
[$controller, 'redirect']
)->setName('redirect');
$app->get(
'/json',
[$controller, 'json']
);
$app->run();

View file

@ -1,17 +1,17 @@
{
"name": "alltube",
"description": "HTML GUI for youtube-dl",
"version": "0.7.2-beta",
"version": "0.7.2-beta2",
"author": "Pierre Rudloff",
"bugs": "https://github.com/Rudloff/alltube/issues",
"dependencies": {
"bower": "~1.8.0",
"grunt": "~1.0.1",
"grunt-contrib-cssmin": "~1.0.0",
"grunt-contrib-uglify": "~2.0.0"
"grunt-contrib-cssmin": "~2.0.0",
"grunt-contrib-uglify": "~2.2.0"
},
"devDependencies": {
"grunt-contrib-compress": "~1.3.0",
"grunt-contrib-compress": "~1.4.1",
"grunt-contrib-watch": "~1.0.0",
"grunt-fixpack": "~0.1.0",
"grunt-githash": "~0.1.3",
@ -27,7 +27,7 @@
"homepage": "https://www.alltubedownload.net/",
"keywords": [
"alltube",
"dowload",
"download",
"video",
"youtube"
],

View file

@ -1,7 +1,8 @@
<phpunit bootstrap="vendor/autoload.php">
<phpunit bootstrap="tests/bootstrap.php">
<filter>
<whitelist>
<directory>classes/</directory>
<directory>controllers/</directory>
</whitelist>
</filter>
<testsuites>

View file

@ -10,7 +10,7 @@
{/if}
<link rel="stylesheet" href="{base_url|noscheme}/dist/main.css" />
<title>AllTube Download{if isset($title)} - {$title|escape}{/if}</title>
<link rel="canonical" href="//{$smarty.server.HTTP_HOST|cat:$smarty.server.REQUEST_URI|replace:{base_url|noscheme}:'http://www.alltubedownload.net'}" />
<link rel="canonical" href="{$canonical}" />
<link rel="icon" href="{base_url|noscheme}/img/favicon.png" />
<meta property="og:title" content="AllTube Download{if isset($title)} - {$title|escape}{/if}" />
<meta property="og:image" content="{base_url}/img/logo.png" />

View file

@ -12,7 +12,7 @@
<input class="URLinput" type="url" name="url" id="url"
required autofocus placeholder="http://example.com/video" />
</span>
{if uglyUrls}
{if $uglyUrls}
<input type="hidden" name="page" value="video" />
{/if}
<input class="downloadBtn" type="submit" value="Download" /><br/>
@ -28,7 +28,7 @@
<a class="combatiblelink" href="{path_for name="extractors"}">See all supported websites</a>
<div id="bookmarklet" class="bookmarklet_wrapper">
<p> Drag this to your bookmarks bar: </p>
<a class="bookmarklet" href="javascript:window.location='{base_url|noscheme}{path_for name='video'}?url='+encodeURIComponent(location.href);">Bookmarklet</a>
<a class="bookmarklet" href="javascript:window.location='{$domain}{path_for name='video'}?url='+encodeURIComponent(location.href);">Bookmarklet</a>
</div>
</div>

View file

@ -29,18 +29,18 @@
{/if}
<select name="format" id="format" class="formats monospace">
<optgroup label="Generic formats">
<option value="best[protocol^=http]">
<option value="best{$protocol}">
{strip}
Best ({$video->ext})
{/strip}
</option>
<option value="worst[protocol^=http]">
<option value="worst{$protocol}">
Worst
</option>
</optgroup>
<optgroup label="Detailed formats" class="monospace">
{foreach $video->formats as $format}
{if $format->protocol|in_array:array('http', 'https')}
{if $config->stream || $format->protocol|in_array:array('http', 'https')}
{strip}
<option value="{$format->format_id}">
{$format->ext}
@ -80,7 +80,7 @@
<input class="downloadBtn" type="submit" value="Download" /><br/>
</form>
{else}
<input type="hidden" name="format" value="best[protocol^=http]" />
<input type="hidden" name="format" value="best{$protocol}" />
<a class="downloadBtn"
href="{$video->url|escape}">Download</a><br/>
{/if}

View file

@ -0,0 +1,370 @@
<?php
/**
* FrontControllerTest class.
*/
namespace Alltube\Test;
use Alltube\Config;
use Alltube\Controller\FrontController;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Unit tests for the FrontController class.
*/
class FrontControllerTest extends \PHPUnit_Framework_TestCase
{
/**
* Slim dependency container.
*
* @var Container
*/
private $container;
/**
* Mock HTTP request.
*
* @var Request
*/
private $request;
/**
* Mock HTTP response.
*
* @var Response
*/
private $response;
/**
* FrontController instance used in tests.
*
* @var FrontController
*/
private $controller;
/**
* Prepare tests.
*/
protected function setUp()
{
$this->container = new Container();
$this->request = Request::createFromEnvironment(Environment::mock());
$this->response = new Response();
$this->container['view'] = function ($c) {
$view = new \Slim\Views\Smarty(__DIR__.'/../templates/');
$smartyPlugins = new \Slim\Views\SmartyPlugins($c['router'], $this->request->getUri());
$view->registerPlugin('function', 'path_for', [$smartyPlugins, 'pathFor']);
$view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);
$view->registerPlugin('modifier', 'noscheme', 'Smarty_Modifier_noscheme');
return $view;
};
$this->controller = new FrontController($this->container);
$this->container['router']->map(['GET'], '/', [$this->controller, 'index'])
->setName('index');
$this->container['router']->map(['GET'], '/video', [$this->controller, 'video'])
->setName('video');
$this->container['router']->map(['GET'], '/extractors', [$this->controller, 'extractors'])
->setName('extractors');
$this->container['router']->map(['GET'], '/redirect', [$this->controller, 'redirect'])
->setName('redirect');
}
/**
* Destroy properties after test.
*/
protected function tearDown()
{
Config::destroyInstance();
}
/**
* Test the constructor with streams enabled.
*
* @return void
*/
public function testConstructorWithStream()
{
$config = Config::getInstance();
$config->stream = true;
$controller = new FrontController($this->container);
$this->assertInstanceOf(FrontController::class, $controller);
}
/**
* Test the index() function.
*
* @return void
*/
public function testIndex()
{
$result = $this->controller->index($this->request, $this->response);
$this->assertTrue($result->isOk());
}
/**
* Test the index() function with a custom URI.
*
* @return void
*/
public function testIndexWithCustomUri()
{
$result = $this->controller->index(
Request::createFromEnvironment(
Environment::mock(['REQUEST_URI'=>'/foo', 'QUERY_STRING'=>'foo=bar'])
),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the extractors() function.
*
* @return void
*/
public function testExtractors()
{
$result = $this->controller->extractors($this->request, $this->response);
$this->assertTrue($result->isOk());
}
/**
* Test the password() function.
*
* @return void
*/
public function testPassword()
{
$result = $this->controller->password($this->request, $this->response);
$this->assertTrue($result->isOk());
}
/**
* Test the video() function without the url parameter.
*
* @return void
*/
public function testVideoWithoutUrl()
{
$result = $this->controller->video($this->request, $this->response);
$this->assertTrue($result->isRedirect());
}
/**
* Test the video() function.
*
* @return void
*/
public function testVideo()
{
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the video() function with audio conversion.
*
* @return void
*/
public function testVideoWithAudio()
{
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio'=>true]),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the video() function with audio enabled and an URL that doesn't need to be converted.
*
* @return void
*/
public function testVideoWithUnconvertedAudio()
{
$result = $this->controller->video(
$this->request->withQueryParams(
['url' => 'https://soundcloud.com/verwandlungskuenstler/metamorphosis-by-franz-kafka-1',
'audio'=> true, ]
),
$this->response
);
$this->assertTrue($result->isRedirect());
}
/**
* Test the video() function with a password.
*
* @return void
*/
public function testVideoWithPassword()
{
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'http://vimeo.com/68375962'])
->withParsedBody(['password'=>'youtube-dl']),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the video() function with a missing password.
*
* @return void
*/
public function testVideoWithMissingPassword()
{
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'http://vimeo.com/68375962']),
$this->response
);
$this->assertTrue($result->isOk());
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'http://vimeo.com/68375962', 'audio'=>true]),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the video() function with streams enabled.
*
* @return void
*/
public function testVideoWithStream()
{
$config = Config::getInstance();
$config->stream = true;
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
$this->response
);
$this->assertTrue($result->isOk());
$result = $this->controller->video(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio'=>true]),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the error() function.
*
* @return void
*/
public function testError()
{
$result = $this->controller->error($this->request, $this->response, new \Exception('foo'));
$this->assertTrue($result->isServerError());
}
/**
* Test the redirect() function without the URL parameter.
*
* @return void
*/
public function testRedirectWithoutUrl()
{
$result = $this->controller->redirect($this->request, $this->response);
$this->assertTrue($result->isRedirect());
}
/**
* Test the redirect() function.
*
* @return void
*/
public function testRedirect()
{
$result = $this->controller->redirect(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
$this->response
);
$this->assertTrue($result->isRedirect());
}
/**
* Test the redirect() function with a specific format.
*
* @return void
*/
public function testRedirectWithFormat()
{
$result = $this->controller->redirect(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format'=>'worst']),
$this->response
);
$this->assertTrue($result->isRedirect());
}
/**
* Test the redirect() function with streams enabled.
*
* @return void
*/
public function testRedirectWithStream()
{
$config = Config::getInstance();
$config->stream = true;
$result = $this->controller->redirect(
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the redirect() function with an M3U stream.
*
* @return void
*/
public function testRedirectWithM3uStream()
{
$config = Config::getInstance();
$config->stream = true;
//We need to create a new controller instance in order to apply the custom config
$controller = new FrontController($this->container);
$result = $controller->redirect(
$this->request->withQueryParams(['url'=>'https://twitter.com/verge/status/813055465324056576/video/1']),
$this->response
);
$this->assertTrue($result->isOk());
}
/**
* Test the redirect() function with a missing password.
*
* @return void
*/
public function testRedirectWithMissingPassword()
{
$result = $this->controller->redirect(
$this->request->withQueryParams(['url'=>'http://vimeo.com/68375962']),
$this->response
);
$this->assertTrue($result->isRedirect());
}
/**
* Test the redirect() function with an error.
*
* @return void
*/
public function testRedirectWithError()
{
$result = $this->controller->redirect(
$this->request->withQueryParams(['url'=>'http://example.com/foo']),
$this->response
);
$this->assertTrue($result->isServerError());
}
}

View file

@ -7,10 +7,7 @@ namespace Alltube\Test;
use Alltube\UglyRouter;
use Slim\Http\Environment;
use Slim\Http\Headers;
use Slim\Http\Request;
use Slim\Http\Stream;
use Slim\Http\Uri;
/**
* Unit tests for the UglyRouter class.
@ -43,13 +40,13 @@ class UglyRouterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(
[1, 'route0', []],
$this->router->dispatch(
new Request(
'GET',
Uri::createFromString('http://example.com/?page=foo'),
Headers::createFromEnvironment(new Environment()),
[],
[],
new Stream(fopen('php://temp', 'r'))
Request::createFromEnvironment(
Environment::mock(
[
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'page=foo',
]
)
)
)
);

View file

@ -146,7 +146,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
{
return [
[
'https://www.youtube.com/watch?v=M7IpKCZ47pU', null,
'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'best[protocol^=http]',
"It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU",
'mp4',
'googlevideo.com',
@ -159,7 +159,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
'googlevideo.com',
],
[
'https://vimeo.com/24195442', null,
'https://vimeo.com/24195442', 'best[protocol^=http]',
'Carving the Mountains-24195442',
'mp4',
'vimeocdn.com',
@ -179,6 +179,23 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
];
}
/**
* Provides M3U8 URLs for tests.
*
* @return array[]
*/
public function m3uUrlProvider()
{
return [
[
'https://twitter.com/verge/status/813055465324056576/video/1', 'best',
'The Verge - This tiny origami robot can self-fold and complete tasks-813055465324056576',
'mp4',
'video.twimg.com',
],
];
}
/**
* Provides incorrect URLs for tests.
*
@ -199,6 +216,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*
* @return void
* @dataProvider URLProvider
* @dataProvider m3uUrlProvider
*/
public function testGetJSON($url, $format)
{
@ -207,6 +225,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
$this->assertObjectHasAttribute('url', $info);
$this->assertObjectHasAttribute('ext', $info);
$this->assertObjectHasAttribute('title', $info);
$this->assertObjectHasAttribute('extractor_key', $info);
$this->assertObjectHasAttribute('formats', $info);
$this->assertObjectHasAttribute('_filename', $info);
}
@ -222,7 +241,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*/
public function testGetJSONError($url)
{
$videoURL = $this->download->getJSON($url);
$this->download->getJSON($url);
}
/**
@ -235,6 +254,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*
* @return void
* @dataProvider urlProvider
* @dataProvider m3uUrlProvider
*/
public function testGetFilename($url, $format, $filename, $extension)
{
@ -259,14 +279,13 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
/**
* Test getAudioFilename function.
*
* @param string $url URL
* @param string $format Format
* @param string $filename Filename
* @param string $domain Domain
* @param string $audioFilename MP3 audio file name
* @param string $url URL
* @param string $format Format
* @param string $filename Filename
*
* @return void
* @dataProvider urlProvider
* @dataProvider m3uUrlProvider
*/
public function testGetAudioFilename($url, $format, $filename)
{
@ -302,7 +321,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*/
public function testGetAudioStreamAvconvError($url, $format)
{
$config = \Alltube\Config::getInstance();
$config = Config::getInstance();
$config->avconv = 'foobar';
$this->download->getAudioStream($url, $format);
}
@ -319,7 +338,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*/
public function testGetAudioStreamCurlError($url, $format)
{
$config = \Alltube\Config::getInstance();
$config = Config::getInstance();
$config->curl = 'foobar';
$config->rtmpdump = 'foobar';
$this->download->getAudioStream($url, $format);
@ -328,11 +347,50 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
/**
* Test getAudioStream function with a M3U8 file.
*
* @param string $url URL
* @param string $format Format
*
* @return void
* @expectedException Exception
* @dataProvider m3uUrlProvider
*/
public function testGetAudioStreamM3uError()
public function testGetAudioStreamM3uError($url, $format)
{
$this->download->getAudioStream('https://twitter.com/verge/status/813055465324056576/video/1', 'best');
$this->download->getAudioStream($url, $format);
}
/**
* Test getM3uStream function.
*
* @param string $url URL
* @param string $format Format
*
* @return void
* @dataProvider m3uUrlProvider
*/
public function testGetM3uStream($url, $format)
{
$video = $this->download->getJSON($url, $format);
$stream = $this->download->getM3uStream($video);
$this->assertInternalType('resource', $stream);
$this->assertFalse(feof($stream));
}
/**
* Test getM3uStream function without avconv.
*
* @param string $url URL
* @param string $format Format
*
* @return void
* @expectedException Exception
* @dataProvider m3uUrlProvider
*/
public function testGetM3uStreamAvconvError($url, $format)
{
$config = \Alltube\Config::getInstance();
$config->avconv = 'foobar';
$video = $this->download->getJSON($url, $format);
$this->download->getM3uStream($video);
}
}

11
tests/bootstrap.php Normal file
View file

@ -0,0 +1,11 @@
<?php
/**
* File used to bootstrap tests.
*/
/**
* Composer autoload.
*/
require_once __DIR__.'/../vendor/autoload.php';
session_start();