Merge branch 'release-0.9.0'
This commit is contained in:
commit
f53fc5ebc1
19 changed files with 704 additions and 280 deletions
15
FAQ.md
15
FAQ.md
|
@ -15,10 +15,10 @@ 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
|
||||
* `remux`: enable remux mode (experimental)
|
||||
|
||||
See [`config.example.yml`](config.example.yml) for default values.
|
||||
|
||||
|
@ -31,10 +31,10 @@ convert: true
|
|||
avconv: path/to/avconv
|
||||
```
|
||||
|
||||
You will also need to install `avconv` and `curl` on your server:
|
||||
You will also need to install `avconv` on your server:
|
||||
|
||||
```bash
|
||||
sudo apt-get install libav-tools curl
|
||||
sudo apt-get install libav-tools
|
||||
```
|
||||
|
||||
## How do I deploy Alltube on Heroku?
|
||||
|
@ -129,3 +129,12 @@ And you probably need to run this in another terminal after `heroku local` has f
|
|||
```bash
|
||||
chmod 0667 /tmp/heroku.fcgi.5000.sock
|
||||
```
|
||||
|
||||
## How can I download 1080p videos from Youtube?
|
||||
|
||||
Youtube distributes HD content in two separate video and audio files.
|
||||
So Alltube will offer you video-only and audio-only formats in the format list.
|
||||
|
||||
You then need to merge them together with a tool like ffmpeg.
|
||||
|
||||
You can also enable the experimental remux mode that will merge the best video and the best audio format on the fly.
|
||||
|
|
|
@ -122,13 +122,13 @@ server {
|
|||
|
||||
## Other dependencies
|
||||
|
||||
You need [avconv](https://libav.org/avconv.html), [rtmpdump](http://rtmpdump.mplayerhq.hu/) and [curl](https://curl.haxx.se/) in order to enable conversions.
|
||||
You need [avconv](https://libav.org/avconv.html) and [rtmpdump](http://rtmpdump.mplayerhq.hu/) in order to enable conversions.
|
||||
If you don't want to enable conversions, you can disable it in `config.yml`.
|
||||
|
||||
On Debian-based systems:
|
||||
|
||||
```bash
|
||||
sudo apt-get install libav-tools rtmpdump curl
|
||||
sudo apt-get install libav-tools rtmpdump
|
||||
```
|
||||
|
||||
You also probably need to edit the `avconv` variable in `config.yml` so that it points to your ffmpeg/avconv binary (`/usr/bin/avconv` on Debian/Ubuntu).
|
||||
|
|
|
@ -38,7 +38,7 @@ class Config
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public $params = ['--no-playlist', '--no-warnings', '--playlist-end', 1];
|
||||
public $params = ['--no-warnings', '--ignore-errors', '--flat-playlist'];
|
||||
|
||||
/**
|
||||
* Enable audio conversion.
|
||||
|
@ -61,20 +61,6 @@ class Config
|
|||
*/
|
||||
public $rtmpdump = 'vendor/bin/rtmpdump';
|
||||
|
||||
/**
|
||||
* curl binary path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $curl = '/usr/bin/curl';
|
||||
|
||||
/**
|
||||
* curl parameters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $curl_params = [];
|
||||
|
||||
/**
|
||||
* Disable URL rewriting.
|
||||
*
|
||||
|
@ -89,6 +75,13 @@ class Config
|
|||
*/
|
||||
public $stream = false;
|
||||
|
||||
/**
|
||||
* Allow to remux video + audio?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $remux = false;
|
||||
|
||||
/**
|
||||
* YAML config file path.
|
||||
*
|
||||
|
@ -104,9 +97,7 @@ class Config
|
|||
* * python: Python binary path
|
||||
* * avconv: avconv or ffmpeg binary path
|
||||
* * rtmpdump: rtmpdump binary path
|
||||
* * curl: curl binary path
|
||||
* * params: Array of youtube-dl parameters
|
||||
* * curl_params: Array of curl parameters
|
||||
* * convert: Enable conversion?
|
||||
*
|
||||
* @param array $options Options
|
||||
|
@ -141,7 +132,7 @@ class Config
|
|||
if (is_null(self::$instance) || self::$instance->file != $yamlfile) {
|
||||
if (is_file($yamlfile)) {
|
||||
$options = Yaml::parse(file_get_contents($yamlPath));
|
||||
} elseif ($yamlfile == 'config.yml') {
|
||||
} elseif ($yamlfile == 'config.yml' || empty($yamlfile)) {
|
||||
/*
|
||||
Allow for the default file to be missing in order to
|
||||
not surprise users that did not create a config file
|
||||
|
|
|
@ -98,7 +98,7 @@ class VideoDownload
|
|||
throw new \Exception($errorOutput);
|
||||
}
|
||||
} else {
|
||||
return $process->getOutput();
|
||||
return trim($process->getOutput());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,21 +113,25 @@ class VideoDownload
|
|||
* */
|
||||
public function getJSON($url, $format = null, $password = null)
|
||||
{
|
||||
return json_decode($this->getProp($url, $format, 'dump-json', $password));
|
||||
return json_decode($this->getProp($url, $format, 'dump-single-json', $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL of video from URL of page.
|
||||
*
|
||||
* It generally returns only one URL.
|
||||
* But it can return two URLs when multiple formats are specified
|
||||
* (eg. bestvideo+bestaudio).
|
||||
*
|
||||
* @param string $url URL of page
|
||||
* @param string $format Format to use for the video
|
||||
* @param string $password Video password
|
||||
*
|
||||
* @return string URL of video
|
||||
* @return string[] URLs of video
|
||||
* */
|
||||
public function getURL($url, $format = null, $password = null)
|
||||
{
|
||||
return $this->getProp($url, $format, 'get-url', $password);
|
||||
return explode(PHP_EOL, $this->getProp($url, $format, 'get-url', $password));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +148,28 @@ class VideoDownload
|
|||
return trim($this->getProp($url, $format, 'get-filename', $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename of video with the specified extension.
|
||||
*
|
||||
* @param string $extension New file extension
|
||||
* @param string $url URL of page
|
||||
* @param string $format Format to use for the video
|
||||
* @param string $password Video password
|
||||
*
|
||||
* @return string Filename of extracted video with specified extension
|
||||
*/
|
||||
public function getFileNameWithExtension($extension, $url, $format = null, $password = null)
|
||||
{
|
||||
return html_entity_decode(
|
||||
pathinfo(
|
||||
$this->getFilename($url, $format, $password),
|
||||
PATHINFO_FILENAME
|
||||
).'.'.$extension,
|
||||
ENT_COMPAT,
|
||||
'ISO-8859-1'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename of audio from URL of page.
|
||||
*
|
||||
|
@ -155,14 +181,7 @@ class VideoDownload
|
|||
* */
|
||||
public function getAudioFilename($url, $format = null, $password = null)
|
||||
{
|
||||
return html_entity_decode(
|
||||
pathinfo(
|
||||
$this->getFilename($url, $format, $password),
|
||||
PATHINFO_FILENAME
|
||||
).'.mp3',
|
||||
ENT_COMPAT,
|
||||
'ISO-8859-1'
|
||||
);
|
||||
return $this->getFileNameWithExtension('mp3', $url, $format, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,31 +241,30 @@ class VideoDownload
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a process that runs curl in order to download a video.
|
||||
* Get a process that runs avconv in order to convert a video to MP3.
|
||||
*
|
||||
* @param object $video Video object returned by youtube-dl
|
||||
* @param string $url URL of the video file
|
||||
*
|
||||
* @return \Symfony\Component\Process\Process Process
|
||||
*/
|
||||
private function getCurlProcess($video)
|
||||
private function getAvconvMp3Process($url)
|
||||
{
|
||||
if (!shell_exec('which '.$this->config->curl)) {
|
||||
throw(new \Exception('Can\'t find curl'));
|
||||
if (!shell_exec('which '.$this->config->avconv)) {
|
||||
throw(new \Exception('Can\'t find avconv or ffmpeg'));
|
||||
}
|
||||
$builder = ProcessBuilder::create(
|
||||
array_merge(
|
||||
[
|
||||
$this->config->curl,
|
||||
'--silent',
|
||||
'--location',
|
||||
'--user-agent', $video->http_headers->{'User-Agent'},
|
||||
$video->url,
|
||||
],
|
||||
$this->config->curl_params
|
||||
)
|
||||
);
|
||||
|
||||
return $builder->getProcess();
|
||||
return ProcessBuilder::create(
|
||||
[
|
||||
$this->config->avconv,
|
||||
'-v', 'quiet',
|
||||
//Vimeo needs a correct user-agent
|
||||
'-user-agent', $this->getProp(null, null, 'dump-user-agent'),
|
||||
'-i', $url,
|
||||
'-f', 'mp3',
|
||||
'-vn',
|
||||
'pipe:1',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -260,40 +278,22 @@ class VideoDownload
|
|||
*/
|
||||
public function getAudioStream($url, $format, $password = null)
|
||||
{
|
||||
if (!shell_exec('which '.$this->config->avconv)) {
|
||||
throw(new \Exception('Can\'t find avconv or ffmpeg'));
|
||||
}
|
||||
|
||||
$video = $this->getJSON($url, $format, $password);
|
||||
if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) {
|
||||
throw(new \Exception('Conversion of M3U8 files is not supported.'));
|
||||
}
|
||||
|
||||
//Vimeo needs a correct user-agent
|
||||
ini_set(
|
||||
'user_agent',
|
||||
$video->http_headers->{'User-Agent'}
|
||||
);
|
||||
$avconvProc = ProcessBuilder::create(
|
||||
[
|
||||
$this->config->avconv,
|
||||
'-v', 'quiet',
|
||||
'-i', '-',
|
||||
'-f', 'mp3',
|
||||
'-vn',
|
||||
'pipe:1',
|
||||
]
|
||||
);
|
||||
|
||||
if (parse_url($video->url, PHP_URL_SCHEME) == 'rtmp') {
|
||||
$process = $this->getRtmpProcess($video);
|
||||
} else {
|
||||
$process = $this->getCurlProcess($video);
|
||||
}
|
||||
$chain = new Chain($process);
|
||||
$chain->add('|', $avconvProc);
|
||||
$chain = new Chain($process);
|
||||
$chain->add('|', $this->getAvconvMp3Process('-'));
|
||||
|
||||
return popen($chain->getProcess()->getCommandLine(), 'r');
|
||||
return popen($chain->getProcess()->getCommandLine(), 'r');
|
||||
} else {
|
||||
$avconvProc = $this->getAvconvMp3Process($video->url);
|
||||
|
||||
return popen($avconvProc->getProcess()->getCommandLine(), 'r');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,4 +324,42 @@ class VideoDownload
|
|||
|
||||
return popen($procBuilder->getProcess()->getCommandLine(), 'r');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an avconv stream to remux audio and video.
|
||||
*
|
||||
* @param array $urls URLs of the video ($urls[0]) and audio ($urls[1]) files
|
||||
*
|
||||
* @return resource popen stream
|
||||
*/
|
||||
public function getRemuxStream(array $urls)
|
||||
{
|
||||
$procBuilder = ProcessBuilder::create(
|
||||
[
|
||||
$this->config->avconv,
|
||||
'-v', 'quiet',
|
||||
'-i', $urls[0],
|
||||
'-i', $urls[1],
|
||||
'-c', 'copy',
|
||||
'-map', '0:v:0 ',
|
||||
'-map', '1:a:0',
|
||||
'-f', 'matroska',
|
||||
'pipe:1',
|
||||
]
|
||||
);
|
||||
|
||||
return popen($procBuilder->getProcess()->getCommandLine(), 'r');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get video stream from an RTMP video.
|
||||
*
|
||||
* @param \stdClass $video Video object returned by getJSON
|
||||
*
|
||||
* @return resource popen stream
|
||||
*/
|
||||
public function getRtmpStream(\stdClass $video)
|
||||
{
|
||||
return popen($this->getRtmpProcess($video)->getCommandLine(), 'r');
|
||||
}
|
||||
}
|
||||
|
|
42
classes/ViewFactory.php
Normal file
42
classes/ViewFactory.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* ViewFactory class.
|
||||
*/
|
||||
|
||||
namespace Alltube;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Views\Smarty;
|
||||
use Slim\Views\SmartyPlugins;
|
||||
|
||||
/**
|
||||
* Create Smarty view object.
|
||||
*/
|
||||
class ViewFactory
|
||||
{
|
||||
/**
|
||||
* Create Smarty view object.
|
||||
*
|
||||
* @param Container $container Slim dependency container
|
||||
* @param Request $request PSR-7 request
|
||||
*
|
||||
* @return Smarty
|
||||
*/
|
||||
public static function create(ContainerInterface $container, Request $request = null)
|
||||
{
|
||||
if (!isset($request)) {
|
||||
$request = $container['request'];
|
||||
}
|
||||
|
||||
$view = new Smarty(__DIR__.'/../templates/');
|
||||
|
||||
$smartyPlugins = new SmartyPlugins($container['router'], $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;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
"type": "project",
|
||||
"require": {
|
||||
"smarty/smarty": "~3.1.29",
|
||||
"slim/slim": "~3.7.0",
|
||||
"slim/slim": "~3.8.1",
|
||||
"mathmarques/smarty-view": "~1.1.0",
|
||||
"symfony/yaml": "~3.2.0",
|
||||
"symfony/process": "~3.2.0",
|
||||
|
@ -21,7 +21,7 @@
|
|||
"squizlabs/php_codesniffer": "~2.8.0",
|
||||
"phpunit/phpunit": "~5.7.2",
|
||||
"ffmpeg/ffmpeg": "dev-release",
|
||||
"rg3/youtube-dl": "~2017.04.15",
|
||||
"rg3/youtube-dl": "~2017.04.28",
|
||||
"rudloff/rtmpdump-bin": "~2.3",
|
||||
"heroku/heroku-buildpack-php": "*"
|
||||
},
|
||||
|
@ -37,10 +37,10 @@
|
|||
"type": "package",
|
||||
"package": {
|
||||
"name": "rg3/youtube-dl",
|
||||
"version": "2017.04.15",
|
||||
"version": "2017.04.28",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.15.zip"
|
||||
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.28.zip"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
19
composer.lock
generated
19
composer.lock
generated
|
@ -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": "863f0c9fafcc85d185aa4441b0ad7e71",
|
||||
"content-hash": "6fda75dec4d72b9620ca68697278dc7e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aura/session",
|
||||
|
@ -752,23 +752,24 @@
|
|||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "3.7.0",
|
||||
"version": "3.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "4254e40d81559e35cdf856bcbaca5f3af468b7ef"
|
||||
"reference": "5385302707530b2bccee1769613ad769859b826d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4254e40d81559e35cdf856bcbaca5f3af468b7ef",
|
||||
"reference": "4254e40d81559e35cdf856bcbaca5f3af468b7ef",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/5385302707530b2bccee1769613ad769859b826d",
|
||||
"reference": "5385302707530b2bccee1769613ad769859b826d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"container-interop/container-interop": "^1.1",
|
||||
"container-interop/container-interop": "^1.2",
|
||||
"nikic/fast-route": "^1.0",
|
||||
"php": ">=5.5.0",
|
||||
"pimple/pimple": "^3.0",
|
||||
"psr/container": "^1.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
|
@ -818,7 +819,7 @@
|
|||
"micro",
|
||||
"router"
|
||||
],
|
||||
"time": "2016-12-20T20:30:47+00:00"
|
||||
"time": "2017-03-19T17:55:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "smarty/smarty",
|
||||
|
@ -1734,10 +1735,10 @@
|
|||
},
|
||||
{
|
||||
"name": "rg3/youtube-dl",
|
||||
"version": "2017.04.15",
|
||||
"version": "2017.04.28",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.15.zip",
|
||||
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.28.zip",
|
||||
"reference": null,
|
||||
"shasum": null
|
||||
},
|
||||
|
|
|
@ -5,10 +5,8 @@ params:
|
|||
- --no-warnings
|
||||
- --playlist-end
|
||||
- 1
|
||||
curl_params:
|
||||
convert: false
|
||||
avconv: vendor/bin/ffmpeg
|
||||
rtmpdump: vendor/bin/rtmpdump
|
||||
curl: /usr/bin/curl
|
||||
uglyUrls: false
|
||||
stream: false
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Alltube\Controller;
|
|||
use Alltube\Config;
|
||||
use Alltube\PasswordException;
|
||||
use Alltube\VideoDownload;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
@ -65,15 +65,21 @@ class FrontController
|
|||
* FrontController constructor.
|
||||
*
|
||||
* @param Container $container Slim dependency container
|
||||
* @param Config $config Config instance
|
||||
* @param array $cookies Cookie array
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
public function __construct(ContainerInterface $container, Config $config = null, array $cookies = [])
|
||||
{
|
||||
$this->config = Config::getInstance();
|
||||
if (isset($config)) {
|
||||
$this->config = $config;
|
||||
} else {
|
||||
$this->config = Config::getInstance();
|
||||
}
|
||||
$this->download = new VideoDownload();
|
||||
$this->container = $container;
|
||||
$this->view = $this->container->get('view');
|
||||
$session_factory = new \Aura\Session\SessionFactory();
|
||||
$session = $session_factory->newInstance($_COOKIE);
|
||||
$session = $session_factory->newInstance($cookies);
|
||||
$this->sessionSegment = $session->getSegment('Alltube\Controller\FrontController');
|
||||
if ($this->config->stream) {
|
||||
$this->defaultFormat = 'best';
|
||||
|
@ -173,9 +179,9 @@ class FrontController
|
|||
if ($this->config->stream) {
|
||||
return $this->getStream($params['url'], 'mp3', $response, $request, $password);
|
||||
} else {
|
||||
$url = $this->download->getURL($params['url'], 'mp3[protocol^=http]', $password);
|
||||
$urls = $this->download->getURL($params['url'], 'mp3[protocol^=http]', $password);
|
||||
|
||||
return $response->withRedirect($url);
|
||||
return $response->withRedirect($urls[0]);
|
||||
}
|
||||
} catch (PasswordException $e) {
|
||||
return $this->password($request, $response);
|
||||
|
@ -218,18 +224,31 @@ class FrontController
|
|||
} else {
|
||||
$protocol = '[protocol^=http]';
|
||||
}
|
||||
if (isset($video->entries)) {
|
||||
$template = 'playlist.tpl';
|
||||
} else {
|
||||
$template = 'video.tpl';
|
||||
}
|
||||
if (isset($video->title)) {
|
||||
$title = $video->title;
|
||||
$description = 'Download "'.$video->title.'" from '.$video->extractor_key;
|
||||
} else {
|
||||
$title = 'Video download';
|
||||
$description = 'Download video from '.$video->extractor_key;
|
||||
}
|
||||
$this->view->render(
|
||||
$response,
|
||||
'video.tpl',
|
||||
$template,
|
||||
[
|
||||
'video' => $video,
|
||||
'class' => 'video',
|
||||
'title' => $video->title,
|
||||
'description' => 'Download "'.$video->title.'" from '.$video->extractor_key,
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'protocol' => $protocol,
|
||||
'config' => $this->config,
|
||||
'canonical' => $this->getCanonicalUrl($request),
|
||||
'uglyUrls' => $this->config->uglyUrls,
|
||||
'remux' => $this->config->remux,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -298,10 +317,16 @@ class FrontController
|
|||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
private function getStream($url, $format, $response, $request, $password = null)
|
||||
private function getStream($url, $format, Response $response, Request $request, $password = null)
|
||||
{
|
||||
$video = $this->download->getJSON($url, $format, $password);
|
||||
if ($video->protocol == 'm3u8') {
|
||||
if ($video->protocol == 'rtmp') {
|
||||
$stream = $this->download->getRtmpStream($video);
|
||||
$response = $response->withHeader('Content-Type', 'video/'.$video->ext);
|
||||
if ($request->isGet()) {
|
||||
$response = $response->withBody(new Stream($stream));
|
||||
}
|
||||
} elseif ($video->protocol == 'm3u8') {
|
||||
$stream = $this->download->getM3uStream($video);
|
||||
$response = $response->withHeader('Content-Type', 'video/'.$video->ext);
|
||||
if ($request->isGet()) {
|
||||
|
@ -316,11 +341,98 @@ class FrontController
|
|||
$response = $response->withBody($stream->getBody());
|
||||
}
|
||||
}
|
||||
$response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"');
|
||||
$response = $response->withHeader(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="'.
|
||||
$this->download->getFilename($url, $format, $password).'"'
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a remuxed stream piped through the server.
|
||||
*
|
||||
* @param array $urls URLs of the video and audio files
|
||||
* @param string $format Requested format
|
||||
* @param Response $response PSR-7 response
|
||||
* @param Request $request PSR-7 request
|
||||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
private function getRemuxStream(array $urls, $format, Response $response, Request $request)
|
||||
{
|
||||
if (!$this->config->remux) {
|
||||
throw new \Exception('You need to enable remux mode to merge two formats.');
|
||||
}
|
||||
$stream = $this->download->getRemuxStream($urls);
|
||||
$response = $response->withHeader('Content-Type', 'video/x-matroska');
|
||||
if ($request->isGet()) {
|
||||
$response = $response->withBody(new Stream($stream));
|
||||
}
|
||||
$webpageUrl = $request->getQueryParam('url');
|
||||
|
||||
return $response->withHeader('Content-Disposition', 'attachment; filename="'.pathinfo(
|
||||
$this->download->getFileNameWithExtension(
|
||||
'mkv',
|
||||
$webpageUrl,
|
||||
$format,
|
||||
$this->sessionSegment->getFlash($webpageUrl)
|
||||
),
|
||||
PATHINFO_FILENAME
|
||||
).'.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 redirect query
|
||||
* Depends on whether we want to stream, remux or simply redirect.
|
||||
*
|
||||
* @param string $url URL of the video
|
||||
* @param string $format Requested format
|
||||
* @param Response $response PSR-7 response
|
||||
* @param Request $request PSR-7 request
|
||||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
private function getRedirectResponse($url, $format, Response $response, Request $request)
|
||||
{
|
||||
$videoUrls = $this->download->getURL(
|
||||
$url,
|
||||
$format,
|
||||
$this->sessionSegment->getFlash($url)
|
||||
);
|
||||
if (count($videoUrls) > 1) {
|
||||
return $this->getRemuxStream($videoUrls, $format, $response, $request);
|
||||
} elseif ($this->config->stream) {
|
||||
return $this->getStream(
|
||||
$url,
|
||||
$format,
|
||||
$response,
|
||||
$request,
|
||||
$this->sessionSegment->getFlash($url)
|
||||
);
|
||||
} else {
|
||||
return $response->withRedirect($videoUrls[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to video file.
|
||||
*
|
||||
|
@ -331,34 +443,14 @@ 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'])) {
|
||||
$url = $request->getQueryParam('url');
|
||||
$format = $this->getFormat($request);
|
||||
if (isset($url)) {
|
||||
try {
|
||||
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 $this->getRedirectResponse($url, $format, $response, $request);
|
||||
} catch (PasswordException $e) {
|
||||
return $response->withRedirect(
|
||||
$this->container->get('router')->pathFor('video').'?url='.urlencode($params['url'])
|
||||
$this->container->get('router')->pathFor('video').'?url='.urlencode($url)
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$response->getBody()->write($e->getMessage());
|
||||
|
|
|
@ -397,10 +397,35 @@ padding:3px;
|
|||
|
||||
|
||||
|
||||
/* Playlists */
|
||||
.playlist-entry .thumb {
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.playlist-entry {
|
||||
clear: both;
|
||||
padding-top: 2em;
|
||||
text-align: left;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.playlist-entry h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.playlist-entry h3 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.playlist-entry h3 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.playlist-entry .downloadBtn {
|
||||
font-size: 16px;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -658,6 +683,16 @@ h1 {
|
|||
text-align:left;
|
||||
}
|
||||
|
||||
.playlist-entry {
|
||||
text-align: center;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.playlist-entry .thumb {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media all and (display-mode: standalone) {
|
||||
|
|
18
index.php
18
index.php
|
@ -4,31 +4,23 @@ require_once __DIR__.'/vendor/autoload.php';
|
|||
use Alltube\Config;
|
||||
use Alltube\Controller\FrontController;
|
||||
use Alltube\UglyRouter;
|
||||
use Alltube\ViewFactory;
|
||||
use Slim\App;
|
||||
|
||||
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
|
||||
header('Location: '.str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
|
||||
die;
|
||||
}
|
||||
|
||||
$app = new \Slim\App();
|
||||
$app = new App();
|
||||
$container = $app->getContainer();
|
||||
$config = Config::getInstance();
|
||||
if ($config->uglyUrls) {
|
||||
$container['router'] = new UglyRouter();
|
||||
}
|
||||
$container['view'] = function ($c) {
|
||||
$view = new \Slim\Views\Smarty(__DIR__.'/templates/');
|
||||
$container['view'] = ViewFactory::create($container);
|
||||
|
||||
$smartyPlugins = new \Slim\Views\SmartyPlugins($c['router'], $c['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;
|
||||
};
|
||||
|
||||
$controller = new FrontController($container);
|
||||
$controller = new FrontController($container, null, $_COOKIE);
|
||||
|
||||
$container['errorHandler'] = [$controller, 'error'];
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"name": "AllTube",
|
||||
"description": "Easily download videos from Youtube, Dailymotion, Vimeo and other websites",
|
||||
"developer": {
|
||||
"name": "Pierre Rudloff",
|
||||
"url": "https://rudloff.pro/"
|
||||
},
|
||||
"icons": {
|
||||
"32": "/img/favicon.png",
|
||||
"60": "/img/logo_60.png",
|
||||
"90": "/img/logo_90.png",
|
||||
"243": "/img/logo_app.png",
|
||||
"250": "/img/logo_250.png"
|
||||
},
|
||||
"default_locale": "en",
|
||||
"launch_path": "/index.php"
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "alltube",
|
||||
"description": "HTML GUI for youtube-dl",
|
||||
"version": "0.8.1-beta",
|
||||
"version": "0.9.0",
|
||||
"author": "Pierre Rudloff",
|
||||
"bugs": "https://github.com/Rudloff/alltube/issues",
|
||||
"dependencies": {
|
||||
|
|
29
templates/playlist.tpl
Normal file
29
templates/playlist.tpl
Normal file
|
@ -0,0 +1,29 @@
|
|||
{include file="inc/head.tpl"}
|
||||
<div class="wrapper">
|
||||
<div class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
<p>Videos extracted from the {if isset($video->title)}<i>
|
||||
<a href="{$video->webpage_url}">
|
||||
{$video->title}</a></i>{/if} playlist:
|
||||
</p>
|
||||
{foreach $video->entries as $video}
|
||||
<div class="playlist-entry">
|
||||
<h3><a target="_blank" href="{strip}
|
||||
{if isset($video->ie_key) and $video->ie_key == Youtube and !filter_var($video->url, FILTER_VALIDATE_URL)}
|
||||
https://www.youtube.com/watch?v=
|
||||
{/if}
|
||||
{$video->url}
|
||||
{/strip}">
|
||||
{if !isset($video->title) and $video->ie_key == YoutubePlaylist}
|
||||
Playlist
|
||||
{else}
|
||||
{$video->title}
|
||||
{/if}
|
||||
</a></h3>
|
||||
<a target="_blank" class="downloadBtn" href="{path_for name="redirect"}?url={$video->url}">Download</a>
|
||||
<a target="_blank" href="{path_for name="video"}?url={$video->url}">More options</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
</div>
|
||||
{include file="inc/footer.tpl"}
|
|
@ -34,6 +34,11 @@
|
|||
Best ({$video->ext})
|
||||
{/strip}
|
||||
</option>
|
||||
{if $remux}
|
||||
<option value="bestvideo+bestaudio">
|
||||
Remux best video with best audio
|
||||
</option>
|
||||
{/if}
|
||||
<option value="worst{$protocol}">
|
||||
Worst
|
||||
</option>
|
||||
|
|
|
@ -45,7 +45,6 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
|
|||
public function testGetInstance()
|
||||
{
|
||||
$this->assertEquals($this->config->convert, false);
|
||||
$this->assertInternalType('array', $this->config->curl_params);
|
||||
$this->assertInternalType('array', $this->config->params);
|
||||
$this->assertInternalType('string', $this->config->youtubedl);
|
||||
$this->assertInternalType('string', $this->config->python);
|
||||
|
@ -64,6 +63,16 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
|
|||
Config::getInstance('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the getInstance function with aen empty filename.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetInstanceWithEmptyFile()
|
||||
{
|
||||
Config::getInstance('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the getInstance function with the CONVERT and PYTHON environment variables.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Alltube\Test;
|
|||
|
||||
use Alltube\Config;
|
||||
use Alltube\Controller\FrontController;
|
||||
use Alltube\ViewFactory;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
|
@ -53,18 +54,8 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
$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['view'] = ViewFactory::create($this->container, $this->request);
|
||||
$this->controller = new FrontController($this->container, Config::getInstance('config_test.yml'));
|
||||
$this->container['router']->map(['GET'], '/', [$this->controller, 'index'])
|
||||
->setName('index');
|
||||
$this->container['router']->map(['GET'], '/video', [$this->controller, 'video'])
|
||||
|
@ -83,6 +74,82 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
Config::destroyInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run controller function with custom query parameters and return the result.
|
||||
*
|
||||
* @param string $request Controller function to call
|
||||
* @param array $params Query parameters
|
||||
* @param Config $config Custom config
|
||||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
private function getRequestResult($request, array $params, Config $config = null)
|
||||
{
|
||||
if (isset($config)) {
|
||||
$controller = new FrontController($this->container, $config);
|
||||
} else {
|
||||
$controller = $this->controller;
|
||||
}
|
||||
|
||||
return $controller->$request(
|
||||
$this->request->withQueryParams($params),
|
||||
$this->response
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that calling controller function with these parameters returns a 200 HTTP response.
|
||||
*
|
||||
* @param string $request Controller function to call
|
||||
* @param array $params Query parameters
|
||||
* @param Config $config Custom config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertRequestIsOk($request, array $params = [], Config $config = null)
|
||||
{
|
||||
$this->assertTrue($this->getRequestResult($request, $params, $config)->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that calling controller function with these parameters returns an HTTP redirect.
|
||||
*
|
||||
* @param string $request Controller function to call
|
||||
* @param array $params Query parameters
|
||||
* @param Config $config Custom config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertRequestIsRedirect($request, array $params = [], Config $config = null)
|
||||
{
|
||||
$this->assertTrue($this->getRequestResult($request, $params, $config)->isRedirect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that calling controller function with these parameters returns an HTTP redirect.
|
||||
*
|
||||
* @param string $request Controller function to call
|
||||
* @param array $params Query parameters
|
||||
* @param Config $config Custom config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertRequestIsServerError($request, array $params = [], Config $config = null)
|
||||
{
|
||||
$this->assertTrue($this->getRequestResult($request, $params, $config)->isServerError());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the constructor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testConstructor()
|
||||
{
|
||||
$controller = new FrontController($this->container);
|
||||
$this->assertInstanceOf(FrontController::class, $controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the constructor with streams enabled.
|
||||
*
|
||||
|
@ -90,9 +157,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testConstructorWithStream()
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
$config->stream = true;
|
||||
$controller = new FrontController($this->container);
|
||||
$controller = new FrontController($this->container, new Config(['stream'=>true]));
|
||||
$this->assertInstanceOf(FrontController::class, $controller);
|
||||
}
|
||||
|
||||
|
@ -103,8 +168,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testIndex()
|
||||
{
|
||||
$result = $this->controller->index($this->request, $this->response);
|
||||
$this->assertTrue($result->isOk());
|
||||
$this->assertRequestIsOk('index');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,8 +194,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testExtractors()
|
||||
{
|
||||
$result = $this->controller->extractors($this->request, $this->response);
|
||||
$this->assertTrue($result->isOk());
|
||||
$this->assertRequestIsOk('extractors');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,8 +204,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testPassword()
|
||||
{
|
||||
$result = $this->controller->password($this->request, $this->response);
|
||||
$this->assertTrue($result->isOk());
|
||||
$this->assertRequestIsOk('password');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,8 +214,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testVideoWithoutUrl()
|
||||
{
|
||||
$result = $this->controller->video($this->request, $this->response);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
$this->assertRequestIsRedirect('video');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,11 +224,17 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testVideo()
|
||||
{
|
||||
$result = $this->controller->video(
|
||||
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
|
||||
$this->response
|
||||
);
|
||||
$this->assertTrue($result->isOk());
|
||||
$this->assertRequestIsOk('video', ['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the video() function with a video that does not have a title.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoWithoutTitle()
|
||||
{
|
||||
$this->assertRequestIsOk('video', ['url'=>'http://html5demos.com/video']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,11 +244,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
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());
|
||||
$this->assertRequestIsOk('video', ['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio'=>true]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,14 +254,10 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testVideoWithUnconvertedAudio()
|
||||
{
|
||||
$result = $this->controller->video(
|
||||
$this->request->withQueryParams(
|
||||
['url' => 'https://2080.bandcamp.com/track/cygnus-x-the-orange-theme-2080-faulty-chip-cover',
|
||||
'audio'=> true, ]
|
||||
),
|
||||
$this->response
|
||||
$this->assertRequestIsRedirect(
|
||||
'video',
|
||||
['url'=> 'https://2080.bandcamp.com/track/cygnus-x-the-orange-theme-2080-faulty-chip-cover', 'audio'=>true]
|
||||
);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,16 +282,8 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
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());
|
||||
$this->assertRequestIsOk('video', ['url'=>'http://vimeo.com/68375962']);
|
||||
$this->assertRequestIsOk('video', ['url'=>'http://vimeo.com/68375962', 'audio'=>true]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,18 +293,26 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
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
|
||||
$config = new Config(['stream'=>true]);
|
||||
$this->assertRequestIsOk('video', ['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU'], $config);
|
||||
$this->assertRequestIsOk(
|
||||
'video',
|
||||
['url'=> 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio'=>true],
|
||||
$config
|
||||
);
|
||||
$this->assertTrue($result->isOk());
|
||||
$result = $this->controller->video(
|
||||
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio'=>true]),
|
||||
$this->response
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the video() function with a playlist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testVideoWithPlaylist()
|
||||
{
|
||||
$this->assertRequestIsOk(
|
||||
'video',
|
||||
['url'=> 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
|
||||
);
|
||||
$this->assertTrue($result->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,8 +333,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testRedirectWithoutUrl()
|
||||
{
|
||||
$result = $this->controller->redirect($this->request, $this->response);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
$this->assertRequestIsRedirect('redirect');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,11 +343,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testRedirect()
|
||||
{
|
||||
$result = $this->controller->redirect(
|
||||
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']),
|
||||
$this->response
|
||||
);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
$this->assertRequestIsRedirect('redirect', ['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,11 +353,10 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testRedirectWithFormat()
|
||||
{
|
||||
$result = $this->controller->redirect(
|
||||
$this->request->withQueryParams(['url'=>'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format'=>'worst']),
|
||||
$this->response
|
||||
$this->assertRequestIsRedirect(
|
||||
'redirect',
|
||||
['url'=> 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format'=>'worst']
|
||||
);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -313,13 +366,11 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
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->assertRequestIsOk(
|
||||
'redirect',
|
||||
['url'=> 'https://www.youtube.com/watch?v=M7IpKCZ47pU'],
|
||||
new Config(['stream'=>true])
|
||||
);
|
||||
$this->assertTrue($result->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,15 +380,58 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
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->assertRequestIsOk(
|
||||
'redirect',
|
||||
['url'=> 'https://twitter.com/verge/status/813055465324056576/video/1'],
|
||||
new Config(['stream'=>true])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the redirect() function with an RTMP stream.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRedirectWithRtmpStream()
|
||||
{
|
||||
$this->assertRequestIsOk(
|
||||
'redirect',
|
||||
['url'=> 'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0'],
|
||||
new Config(['stream'=>true])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the redirect() function with a remuxed video.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRedirectWithRemux()
|
||||
{
|
||||
$this->assertRequestIsOk(
|
||||
'redirect',
|
||||
[
|
||||
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
|
||||
'format'=> 'bestvideo+bestaudio',
|
||||
],
|
||||
new Config(['remux'=>true])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the redirect() function with a remuxed video but remux disabled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRedirectWithRemuxDisabled()
|
||||
{
|
||||
$this->assertRequestIsServerError(
|
||||
'redirect',
|
||||
[
|
||||
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
|
||||
'format'=> 'bestvideo+bestaudio',
|
||||
]
|
||||
);
|
||||
$this->assertTrue($result->isOk());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,11 +441,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testRedirectWithMissingPassword()
|
||||
{
|
||||
$result = $this->controller->redirect(
|
||||
$this->request->withQueryParams(['url'=>'http://vimeo.com/68375962']),
|
||||
$this->response
|
||||
);
|
||||
$this->assertTrue($result->isRedirect());
|
||||
$this->assertRequestIsRedirect('redirect', ['url'=>'http://vimeo.com/68375962']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,10 +451,6 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testRedirectWithError()
|
||||
{
|
||||
$result = $this->controller->redirect(
|
||||
$this->request->withQueryParams(['url'=>'http://example.com/foo']),
|
||||
$this->response
|
||||
);
|
||||
$this->assertTrue($result->isServerError());
|
||||
$this->assertRequestIsServerError('redirect', ['url'=>'http://example.com/foo']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$this->download = new VideoDownload();
|
||||
$this->download = new VideoDownload(Config::getInstance('config_test.yml'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,11 +84,14 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*
|
||||
* @return void
|
||||
* @dataProvider urlProvider
|
||||
* @dataProvider m3uUrlProvider
|
||||
* @dataProvider rtmpUrlProvider
|
||||
* @dataProvider remuxUrlProvider
|
||||
*/
|
||||
public function testGetURL($url, $format, $filename, $extension, $domain)
|
||||
{
|
||||
$videoURL = $this->download->getURL($url, $format);
|
||||
$this->assertContains($domain, $videoURL);
|
||||
$this->assertContains($domain, $videoURL[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,7 +101,8 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testGetURLWithPassword()
|
||||
{
|
||||
$this->assertContains('vimeocdn.com', $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl'));
|
||||
$videoURL = $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl');
|
||||
$this->assertContains('vimeocdn.com', $videoURL[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,6 +188,23 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function remuxUrlProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'bestvideo+bestaudio',
|
||||
"It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU",
|
||||
'mp4',
|
||||
'googlevideo.com',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides URLs for remux tests.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function m3uUrlProvider()
|
||||
{
|
||||
return [
|
||||
|
@ -196,6 +217,23 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides RTMP URLs for tests.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function rtmpUrlProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0', 'bestaudio/best',
|
||||
'GRIP sucht den Sommerkönig-folge-203-0',
|
||||
'f4v',
|
||||
'edgefcs.net',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides incorrect URLs for tests.
|
||||
*
|
||||
|
@ -215,8 +253,9 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
* @param string $format Format
|
||||
*
|
||||
* @return void
|
||||
* @dataProvider URLProvider
|
||||
* @dataProvider urlProvider
|
||||
* @dataProvider m3uUrlProvider
|
||||
* @dataProvider rtmpUrlProvider
|
||||
*/
|
||||
public function testGetJSON($url, $format)
|
||||
{
|
||||
|
@ -227,7 +266,6 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertObjectHasAttribute('title', $info);
|
||||
$this->assertObjectHasAttribute('extractor_key', $info);
|
||||
$this->assertObjectHasAttribute('formats', $info);
|
||||
$this->assertObjectHasAttribute('_filename', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,6 +293,8 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
* @return void
|
||||
* @dataProvider urlProvider
|
||||
* @dataProvider m3uUrlProvider
|
||||
* @dataProvider rtmpUrlProvider
|
||||
* @dataProvider remuxUrlProvider
|
||||
*/
|
||||
public function testGetFilename($url, $format, $filename, $extension)
|
||||
{
|
||||
|
@ -286,6 +326,8 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
* @return void
|
||||
* @dataProvider urlProvider
|
||||
* @dataProvider m3uUrlProvider
|
||||
* @dataProvider rtmpUrlProvider
|
||||
* @dataProvider remuxUrlProvider
|
||||
*/
|
||||
public function testGetAudioFilename($url, $format, $filename)
|
||||
{
|
||||
|
@ -321,9 +363,8 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testGetAudioStreamAvconvError($url, $format)
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
$config->avconv = 'foobar';
|
||||
$this->download->getAudioStream($url, $format);
|
||||
$download = new VideoDownload(new Config(['avconv'=>'foobar']));
|
||||
$download->getAudioStream($url, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -334,14 +375,12 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*
|
||||
* @return void
|
||||
* @expectedException Exception
|
||||
* @dataProvider urlProvider
|
||||
* @dataProvider rtmpUrlProvider
|
||||
*/
|
||||
public function testGetAudioStreamCurlError($url, $format)
|
||||
public function testGetAudioStreamRtmpError($url, $format)
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
$config->curl = 'foobar';
|
||||
$config->rtmpdump = 'foobar';
|
||||
$this->download->getAudioStream($url, $format);
|
||||
$download = new VideoDownload(new Config(['rtmpdump'=>'foobar']));
|
||||
$download->getAudioStream($url, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -359,6 +398,19 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
$this->download->getAudioStream($url, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a stream is valid.
|
||||
*
|
||||
* @param resource $stream Stream
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertStream($stream)
|
||||
{
|
||||
$this->assertInternalType('resource', $stream);
|
||||
$this->assertFalse(feof($stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getM3uStream function.
|
||||
*
|
||||
|
@ -370,10 +422,46 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testGetM3uStream($url, $format)
|
||||
{
|
||||
$video = $this->download->getJSON($url, $format);
|
||||
$stream = $this->download->getM3uStream($video);
|
||||
$this->assertInternalType('resource', $stream);
|
||||
$this->assertFalse(feof($stream));
|
||||
$this->assertStream(
|
||||
$this->download->getM3uStream(
|
||||
$this->download->getJSON($url, $format)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getRemuxStream function.
|
||||
*
|
||||
* @param string $url URL
|
||||
* @param string $format Format
|
||||
*
|
||||
* @return void
|
||||
* @dataProvider remuxUrlProvider
|
||||
*/
|
||||
public function testGetRemuxStream($url, $format)
|
||||
{
|
||||
$urls = $this->download->getURL($url, $format);
|
||||
if (count($urls) > 1) {
|
||||
$this->assertStream($this->download->getRemuxStream($urls));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getRtmpStream function.
|
||||
*
|
||||
* @param string $url URL
|
||||
* @param string $format Format
|
||||
*
|
||||
* @return void
|
||||
* @dataProvider rtmpUrlProvider
|
||||
*/
|
||||
public function testGetRtmpStream($url, $format)
|
||||
{
|
||||
$this->assertStream(
|
||||
$this->download->getRtmpStream(
|
||||
$this->download->getJSON($url, $format)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,9 +476,8 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testGetM3uStreamAvconvError($url, $format)
|
||||
{
|
||||
$config = \Alltube\Config::getInstance();
|
||||
$config->avconv = 'foobar';
|
||||
$video = $this->download->getJSON($url, $format);
|
||||
$this->download->getM3uStream($video);
|
||||
$download = new VideoDownload(new Config(['avconv'=>'foobar']));
|
||||
$video = $download->getJSON($url, $format);
|
||||
$download->getM3uStream($video);
|
||||
}
|
||||
}
|
||||
|
|
27
tests/ViewFactoryTest.php
Normal file
27
tests/ViewFactoryTest.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* ViewFactoryTest class.
|
||||
*/
|
||||
|
||||
namespace Alltube\Test;
|
||||
|
||||
use Alltube\ViewFactory;
|
||||
use Slim\Container;
|
||||
use Slim\Views\Smarty;
|
||||
|
||||
/**
|
||||
* Unit tests for the ViewFactory class.
|
||||
*/
|
||||
class ViewFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Test the create() function.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCreate()
|
||||
{
|
||||
$view = ViewFactory::create(new Container());
|
||||
$this->assertInstanceOf(Smarty::class, $view);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue