diff --git a/controllers/BaseController.php b/controllers/BaseController.php new file mode 100644 index 0000000..5af14bf --- /dev/null +++ b/controllers/BaseController.php @@ -0,0 +1,112 @@ +config = Config::getInstance(); + $this->container = $container; + $session_factory = new SessionFactory(); + $session = $session_factory->newInstance($cookies); + $this->sessionSegment = $session->getSegment(self::class); + + if ($this->config->stream) { + $this->defaultFormat = 'best'; + } + } + + /** + * Get video format from request parameters or default format if none is specified. + * + * @param Request $request PSR-7 request + * + * @return string format + */ + protected function getFormat(Request $request) + { + $format = $request->getQueryParam('format'); + if (!isset($format)) { + $format = $this->defaultFormat; + } + + return $format; + } + + /** + * Get the password entered for the current video. + * + * @param Request $request PSR-7 request + * + * @return string Password + */ + protected function getPassword(Request $request) + { + $url = $request->getQueryParam('url'); + + $password = $request->getParam('password'); + if (isset($password)) { + $this->sessionSegment->setFlash($url, $password); + } else { + $password = $this->sessionSegment->getFlash($url); + } + + return $password; + } +} diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php new file mode 100644 index 0000000..ecd7496 --- /dev/null +++ b/controllers/DownloadController.php @@ -0,0 +1,274 @@ +getQueryParam('url'); + + if (isset($url)) { + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); + + try { + if ($this->config->convert && $request->getQueryParam('audio')) { + // Audio convert. + return $this->getAudioResponse($request, $response); + } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { + // Advance convert. + return $this->getConvertedResponse($request, $response); + } + + // Regular download. + return $this->getDownloadResponse($request, $response); + } catch (PasswordException $e) { + return $response->withRedirect( + $this->container->get('router')->pathFor('info').'?'.http_build_query($request->getQueryParams()) + ); + } catch (Exception $e) { + $response->getBody()->write($e->getMessage()); + + return $response->withHeader('Content-Type', 'text/plain')->withStatus(500); + } + } else { + return $response->withRedirect($this->container->get('router')->pathFor('index')); + } + } + + /** + * Return a converted MP3 file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getConvertedAudioResponse(Request $request, Response $response) + { + $from = $request->getQueryParam('from'); + $to = $request->getQueryParam('to'); + + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFileNameWithExtension('mp3').'"' + ); + $response = $response->withHeader('Content-Type', 'audio/mpeg'); + + if ($request->isGet() || $request->isPost()) { + try { + $process = $this->video->getAudioStream($from, $to); + } catch (Exception $e) { + // Fallback to default format. + $this->video = $this->video->withFormat($this->defaultFormat); + $process = $this->video->getAudioStream($from, $to); + } + $response = $response->withBody(new Stream($process)); + } + + return $response; + } + + /** + * Return the MP3 file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getAudioResponse(Request $request, Response $response) + { + try { + // First, we try to get a MP3 file directly. + if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { + throw new Exception('Force convert when we need to seek.'); + } + + if ($this->config->stream) { + $this->video = $this->video->withFormat('mp3'); + + return $this->getStream($request, $response); + } else { + $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]'); + + $urls = $this->video->getUrl(); + + return $response->withRedirect($urls[0]); + } + } catch (PasswordException $e) { + $frontController = new FrontController($this->container); + + return $frontController->password($request, $response); + } catch (Exception $e) { + // If MP3 is not available, we convert it. + $this->video = $this->video->withFormat($this->defaultFormat); + + return $this->getConvertedAudioResponse($request, $response); + } + } + + /** + * Get a video/audio stream piped through the server. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getStream(Request $request, Response $response) + { + if (isset($this->video->entries)) { + if ($this->config->convert && $request->getQueryParam('audio')) { + $stream = new ConvertedPlaylistArchiveStream($this->video); + } else { + $stream = new PlaylistArchiveStream($this->video); + } + $response = $response->withHeader('Content-Type', 'application/zip'); + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'.$this->video->title.'.zip"' + ); + + return $response->withBody($stream); + } elseif ($this->video->protocol == 'rtmp') { + $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); + $body = new Stream($this->video->getRtmpStream()); + } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') { + $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); + $body = new Stream($this->video->getM3uStream()); + } else { + $stream = $this->video->getHttpResponse(['Range' => $request->getHeader('Range')]); + + $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); + $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); + $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges')); + $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range')); + if ($stream->getStatusCode() == 206) { + $response = $response->withStatus(206); + } + $body = $stream->getBody(); + } + if ($request->isGet()) { + $response = $response->withBody($body); + } + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFilename().'"' + ); + + return $response; + } + + /** + * Get a remuxed stream piped through the server. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getRemuxStream(Request $request, Response $response) + { + if (!$this->config->remux) { + throw new Exception(_('You need to enable remux mode to merge two formats.')); + } + $stream = $this->video->getRemuxStream(); + $response = $response->withHeader('Content-Type', 'video/x-matroska'); + if ($request->isGet()) { + $response = $response->withBody(new Stream($stream)); + } + + return $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv') + ); + } + + /** + * Get approriate HTTP response to download query. + * Depends on whether we want to stream, remux or simply redirect. + * + * @param Response $response PSR-7 response + * @param Request $request PSR-7 request + * + * @return Response HTTP response + */ + private function getDownloadResponse(Request $request, Response $response) + { + try { + $videoUrls = $this->video->getUrl(); + } catch (EmptyUrlException $e) { + /* + If this happens it is probably a playlist + so it will either be handled by getStream() or throw an exception anyway. + */ + $videoUrls = []; + } + if (count($videoUrls) > 1) { + return $this->getRemuxStream($request, $response); + } elseif ($this->config->stream) { + return $this->getStream($request, $response); + } else { + if (empty($videoUrls[0])) { + throw new Exception(_("Can't find URL of video.")); + } + + return $response->withRedirect($videoUrls[0]); + } + } + + /** + * Return a converted video file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * + * @return Response HTTP response + */ + private function getConvertedResponse(Request $request, Response $response) + { + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"' + ); + $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat')); + + if ($request->isGet() || $request->isPost()) { + $process = $this->video->getConvertedStream( + $request->getQueryParam('customBitrate'), + $request->getQueryParam('customFormat') + ); + $response = $response->withBody(new Stream($process)); + } + + return $response; + } +} diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 87ee5e8..7a46b7f 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -6,15 +6,11 @@ namespace Alltube\Controller; use Alltube\Config; -use Alltube\ConvertedPlaylistArchiveStream; use Alltube\EmptyUrlException; use Alltube\Locale; use Alltube\LocaleManager; use Alltube\PasswordException; -use Alltube\PlaylistArchiveStream; use Alltube\Video; -use Aura\Session\Segment; -use Aura\Session\SessionFactory; use Exception; use Psr\Container\ContainerInterface; use Slim\Container; @@ -26,36 +22,8 @@ use Slim\Views\Smarty; /** * Main controller. */ -class FrontController +class FrontController extends BaseController { - /** - * Config instance. - * - * @var Config - */ - private $config; - - /** - * Current video. - * - * @var Video - */ - private $video; - - /** - * Slim dependency container. - * - * @var ContainerInterface - */ - private $container; - - /** - * Session segment used to store session variables. - * - * @var Segment - */ - private $sessionSegment; - /** * Smarty view. * @@ -63,13 +31,6 @@ class FrontController */ private $view; - /** - * Default youtube-dl format. - * - * @var string - */ - private $defaultFormat = 'best[protocol=https]/best[protocol=http]'; - /** * LocaleManager instance. * @@ -78,23 +39,17 @@ class FrontController private $localeManager; /** - * FrontController constructor. + * BaseController constructor. * * @param ContainerInterface $container Slim dependency container * @param array $cookies Cookie array */ public function __construct(ContainerInterface $container, array $cookies = []) { - $this->config = Config::getInstance(); - $this->container = $container; - $this->view = $this->container->get('view'); + parent::__construct($container, $cookies); + $this->localeManager = $this->container->get('locale'); - $session_factory = new SessionFactory(); - $session = $session_factory->newInstance($cookies); - $this->sessionSegment = $session->getSegment(self::class); - if ($this->config->stream) { - $this->defaultFormat = 'best'; - } + $this->view = $this->container->get('view'); } /** @@ -195,77 +150,6 @@ class FrontController return $response; } - /** - * Return a converted MP3 file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - private function getConvertedAudioResponse(Request $request, Response $response) - { - $from = $request->getQueryParam('from'); - $to = $request->getQueryParam('to'); - - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->video->getFileNameWithExtension('mp3').'"' - ); - $response = $response->withHeader('Content-Type', 'audio/mpeg'); - - if ($request->isGet() || $request->isPost()) { - try { - $process = $this->video->getAudioStream($from, $to); - } catch (Exception $e) { - // Fallback to default format. - $this->video = $this->video->withFormat($this->defaultFormat); - $process = $this->video->getAudioStream($from, $to); - } - $response = $response->withBody(new Stream($process)); - } - - return $response; - } - - /** - * Return the MP3 file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - private function getAudioResponse(Request $request, Response $response) - { - try { - // First, we try to get a MP3 file directly. - if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { - throw new Exception('Force convert when we need to seek.'); - } - - if ($this->config->stream) { - $this->video = $this->video->withFormat('mp3'); - - return $this->getStream($request, $response); - } else { - $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]'); - - $urls = $this->video->getUrl(); - - return $response->withRedirect($urls[0]); - } - } catch (PasswordException $e) { - return $this->password($request, $response); - } catch (Exception $e) { - // If MP3 is not available, we convert it. - $this->video = $this->video->withFormat($this->defaultFormat); - - return $this->getConvertedAudioResponse($request, $response); - } - } - /** * Return the video description page. * @@ -324,12 +208,7 @@ class FrontController $url = $request->getQueryParam('url') ?: $request->getQueryParam('v'); if (isset($url) && !empty($url)) { - $password = $request->getParam('password'); - if (isset($password)) { - $this->sessionSegment->setFlash($url, $password); - } - - $this->video = new Video($url, $this->defaultFormat, $password); + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); if ($this->config->convert && $request->getQueryParam('audio')) { // We skip the info page and get directly to the download. @@ -372,231 +251,6 @@ class FrontController return $response->withStatus(500); } - /** - * Get a video/audio stream piped through the server. - * - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * - * @return Response HTTP response - */ - private function getStream(Request $request, Response $response) - { - if (isset($this->video->entries)) { - if ($this->config->convert && $request->getQueryParam('audio')) { - $stream = new ConvertedPlaylistArchiveStream($this->video); - } else { - $stream = new PlaylistArchiveStream($this->video); - } - $response = $response->withHeader('Content-Type', 'application/zip'); - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'.$this->video->title.'.zip"' - ); - - return $response->withBody($stream); - } elseif ($this->video->protocol == 'rtmp') { - $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); - $body = new Stream($this->video->getRtmpStream()); - } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') { - $response = $response->withHeader('Content-Type', 'video/'.$this->video->ext); - $body = new Stream($this->video->getM3uStream()); - } else { - $stream = $this->video->getHttpResponse(['Range' => $request->getHeader('Range')]); - - $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); - $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); - $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges')); - $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range')); - if ($stream->getStatusCode() == 206) { - $response = $response->withStatus(206); - } - $body = $stream->getBody(); - } - if ($request->isGet()) { - $response = $response->withBody($body); - } - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->video->getFilename().'"' - ); - - return $response; - } - - /** - * Get a remuxed stream piped through the server. - * - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * - * @return Response HTTP response - */ - private function getRemuxStream(Request $request, Response $response) - { - if (!$this->config->remux) { - throw new Exception(_('You need to enable remux mode to merge two formats.')); - } - $stream = $this->video->getRemuxStream(); - $response = $response->withHeader('Content-Type', 'video/x-matroska'); - if ($request->isGet()) { - $response = $response->withBody(new Stream($stream)); - } - - return $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'.$this->video->getFileNameWithExtension('mkv') - ); - } - - /** - * Get video format from request parameters or default format if none is specified. - * - * @param Request $request PSR-7 request - * - * @return string format - */ - private function getFormat(Request $request) - { - $format = $request->getQueryParam('format'); - if (!isset($format)) { - $format = $this->defaultFormat; - } - - return $format; - } - - /** - * Get approriate HTTP response to download query. - * Depends on whether we want to stream, remux or simply redirect. - * - * @param Response $response PSR-7 response - * @param Request $request PSR-7 request - * - * @return Response HTTP response - */ - private function getDownloadResponse(Request $request, Response $response) - { - try { - $videoUrls = $this->video->getUrl(); - } catch (EmptyUrlException $e) { - /* - If this happens it is probably a playlist - so it will either be handled by getStream() or throw an exception anyway. - */ - $videoUrls = []; - } - if (count($videoUrls) > 1) { - return $this->getRemuxStream($request, $response); - } elseif ($this->config->stream) { - return $this->getStream($request, $response); - } else { - if (empty($videoUrls[0])) { - throw new Exception(_("Can't find URL of video.")); - } - - return $response->withRedirect($videoUrls[0]); - } - } - - /** - * Return a converted video file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - private function getConvertedResponse(Request $request, Response $response) - { - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->video->getFileNameWithExtension($request->getQueryParam('customFormat')).'"' - ); - $response = $response->withHeader('Content-Type', 'video/'.$request->getQueryParam('customFormat')); - - if ($request->isGet() || $request->isPost()) { - $process = $this->video->getConvertedStream( - $request->getQueryParam('customBitrate'), - $request->getQueryParam('customFormat') - ); - $response = $response->withBody(new Stream($process)); - } - - return $response; - } - - /** - * Redirect to video file. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - public function download(Request $request, Response $response) - { - $format = $this->getFormat($request); - $url = $request->getQueryParam('url'); - - if (isset($url)) { - $this->video = new Video($url, $format, $this->sessionSegment->getFlash($url)); - - try { - if ($this->config->convert && $request->getQueryParam('audio')) { - // Audio convert. - return $this->getAudioResponse($request, $response); - } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { - // Advance convert. - return $this->getConvertedResponse($request, $response); - } - - // Regular download. - return $this->getDownloadResponse($request, $response); - } catch (PasswordException $e) { - return $response->withRedirect( - $this->container->get('router')->pathFor('info').'?url='.urlencode($url) - ); - } catch (Exception $e) { - $response->getBody()->write($e->getMessage()); - - return $response->withHeader('Content-Type', 'text/plain')->withStatus(500); - } - } else { - return $response->withRedirect($this->container->get('router')->pathFor('index')); - } - } - - /** - * Return the JSON object generated by youtube-dl. - * - * @param Request $request PSR-7 request - * @param Response $response PSR-7 response - * - * @return Response HTTP response - */ - public function json(Request $request, Response $response) - { - $format = $this->getFormat($request); - $url = $request->getQueryParam('url'); - - if (isset($url)) { - try { - $this->video = new Video($url, $format); - - return $response->withJson($this->video->getJson()); - } catch (Exception $e) { - return $response->withJson(['error' => $e->getMessage()]) - ->withStatus(500); - } - } else { - return $response->withJson(['error' => 'You need to provide the url parameter']) - ->withStatus(400); - } - } - /** * Generate the canonical URL of the current page. * diff --git a/controllers/JsonController.php b/controllers/JsonController.php new file mode 100644 index 0000000..cc36e46 --- /dev/null +++ b/controllers/JsonController.php @@ -0,0 +1,44 @@ +getQueryParam('url'); + + if (isset($url)) { + try { + $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); + + return $response->withJson($this->video->getJson()); + } catch (Exception $e) { + return $response->withJson(['error' => $e->getMessage()]) + ->withStatus(500); + } + } else { + return $response->withJson(['error' => 'You need to provide the url parameter']) + ->withStatus(400); + } + } +} diff --git a/index.php b/index.php index 43f7e8b..9e62d10 100644 --- a/index.php +++ b/index.php @@ -3,6 +3,8 @@ require_once __DIR__.'/vendor/autoload.php'; use Alltube\Config; use Alltube\Controller\FrontController; +use Alltube\Controller\JsonController; +use Alltube\Controller\DownloadController; use Alltube\LocaleManager; use Alltube\LocaleMiddleware; use Alltube\UglyRouter; @@ -32,49 +34,52 @@ if (!class_exists('Locale')) { $container['locale'] = new LocaleManager($_COOKIE); $app->add(new LocaleMiddleware($container)); -$controller = new FrontController($container, $_COOKIE); +$frontController = new FrontController($container, $_COOKIE); +$jsonController = new JsonController($container, $_COOKIE); +$downloadController = new DownloadController($container, $_COOKIE); -$container['errorHandler'] = [$controller, 'error']; +$container['errorHandler'] = [$jsonController, 'error']; $app->get( '/', - [$controller, 'index'] + [$frontController, 'index'] )->setName('index'); $app->get( '/extractors', - [$controller, 'extractors'] + [$frontController, 'extractors'] )->setName('extractors'); $app->any( '/info', - [$controller, 'info'] + [$frontController, 'info'] )->setName('info'); // Legacy route. -$app->any('/video', [$controller, 'info']); +$app->any('/video', [$frontController, 'info']); $app->any( '/watch', - [$controller, 'video'] + [$frontController, 'video'] ); -$app->get( +$app->any( '/download', - [$controller, 'download'] + [$downloadController, 'download'] )->setName('download'); // Legacy route. -$app->get('/redirect', [$controller, 'download']); - -$app->get( - '/json', - [$controller, 'json'] -)->setName('json'); +$app->get('/redirect', [$downloadController, 'download']); $app->get( '/locale/{locale}', - [$controller, 'locale'] + [$frontController, 'locale'] )->setName('locale'); + +$app->get( + '/json', + [$jsonController, 'json'] +)->setName('json'); + try { $app->run(); } catch (SmartyException $e) { diff --git a/tests/BaseTest.php b/tests/BaseTest.php index d3f7796..5499101 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -9,7 +9,7 @@ use Alltube\Config; use PHPUnit\Framework\TestCase; /** - * Unit tests for the ViewFactory class. + * Abstract class used by every test. */ abstract class BaseTest extends TestCase { diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php new file mode 100644 index 0000000..d11af57 --- /dev/null +++ b/tests/ControllerTest.php @@ -0,0 +1,148 @@ +container = new Container(); + $this->request = Request::createFromEnvironment(Environment::mock()); + $this->response = new Response(); + $this->container['view'] = ViewFactory::create($this->container, $this->request); + $this->container['locale'] = new LocaleManager(); + + $frontController = new FrontController($this->container); + $downloadController = new DownloadController($this->container); + + $this->container['router']->map(['GET'], '/', [$frontController, 'index']) + ->setName('index'); + $this->container['router']->map(['GET'], '/video', [$frontController, 'info']) + ->setName('info'); + $this->container['router']->map(['GET'], '/extractors', [$frontController, 'extractors']) + ->setName('extractors'); + $this->container['router']->map(['GET'], '/locale', [$frontController, 'locale']) + ->setName('locale'); + $this->container['router']->map(['GET'], '/redirect', [$downloadController, 'download']) + ->setName('download'); + } + + /** + * Run controller function with custom query parameters and return the result. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return Response HTTP response + */ + protected function getRequestResult($request, array $params) + { + return $this->controller->$request( + $this->request->withQueryParams($params), + $this->response + ); + } + + /** + * Assert that calling controller function with these parameters returns a 200 HTTP response. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsOk($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isOk()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP redirect. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsRedirect($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isRedirect()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP 500 error. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsServerError($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isServerError()); + } + + /** + * Assert that calling controller function with these parameters returns an HTTP 400 error. + * + * @param string $request Controller function to call + * @param array $params Query parameters + * + * @return void + */ + protected function assertRequestIsClientError($request, array $params = []) + { + $this->assertTrue($this->getRequestResult($request, $params)->isClientError()); + } +} diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php new file mode 100644 index 0000000..1e1bb93 --- /dev/null +++ b/tests/DownloadControllerTest.php @@ -0,0 +1,228 @@ +controller = new DownloadController($this->container); + } + + /** + * Test the download() function without the URL parameter. + * + * @return void + */ + public function testDownloadWithoutUrl() + { + $this->assertRequestIsRedirect('download'); + } + + /** + * Test the download() function. + * + * @return void + */ + public function testDownload() + { + $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); + } + + /** + * Test the download() function with a specific format. + * + * @return void + */ + public function testDownloadWithFormat() + { + $this->assertRequestIsRedirect( + 'download', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst'] + ); + } + + /** + * Test the download() function with streams enabled. + * + * @return void + */ + public function testDownloadWithStream() + { + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'] + ); + } + + /** + * Test the download() function with an M3U stream. + * + * @return void + */ + public function testDownloadWithM3uStream() + { + if (getenv('CI')) { + $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.'); + } + + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', + 'format' => 'hls-2176', + ] + ); + } + + /** + * Test the download() function with an RTMP stream. + * + * @return void + */ + public function testDownloadWithRtmpStream() + { + $this->markTestIncomplete('We need to find another RTMP video.'); + + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'] + ); + } + + /** + * Test the download() function with a remuxed video. + * + * @return void + */ + public function testDownloadWithRemux() + { + Config::setOptions(['remux' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'bestvideo+bestaudio', + ] + ); + } + + /** + * Test the download() function with a remuxed video but remux disabled. + * + * @return void + */ + public function testDownloadWithRemuxDisabled() + { + $this->assertRequestIsServerError( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'bestvideo+bestaudio', + ] + ); + } + + /** + * Test the download() function with a missing password. + * + * @return void + */ + public function testDownloadWithMissingPassword() + { + if (getenv('CI')) { + $this->markTestSkipped('Travis is blacklisted by Vimeo.'); + } + $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']); + } + + /** + * Test the download() function with an error. + * + * @return void + */ + public function testDownloadWithError() + { + $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']); + } + + /** + * Test the download() function with an video that returns an empty URL. + * This can be caused by trying to redirect to a playlist. + * + * @return void + */ + public function testDownloadWithEmptyUrl() + { + $this->assertRequestIsServerError( + 'download', + ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] + ); + } + + /** + * Test the download() function with a playlist stream. + * + * @return void + * @requires OS Linux + */ + public function testDownloadWithPlaylist() + { + Config::setOptions(['stream' => true]); + + $this->assertRequestIsOk( + 'download', + ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] + ); + } + + /** + * Test the download() function with an advanced conversion. + * + * @return void + */ + public function testDownloadWithAdvancedConversion() + { + Config::setOptions(['convertAdvanced' => true]); + + $this->assertRequestIsOk( + 'download', + [ + 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', + 'format' => 'best', + 'customConvert' => 'on', + 'customBitrate' => 32, + 'customFormat' => 'flv', + ] + ); + } +} diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 07e1c5e..edc7e58 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -18,35 +18,15 @@ use Slim\Http\Response; /** * Unit tests for the FrontController class. */ -class FrontControllerTest extends BaseTest +class FrontControllerTest extends ControllerTest { - /** - * Slim dependency container. - * - * @var Container - */ - private $container; /** - * Mock HTTP request. - * - * @var Request - */ - private $request; - - /** - * Mock HTTP response. - * - * @var Response - */ - private $response; - - /** - * FrontController instance used in tests. + * Controller instance used in tests. * * @var FrontController */ - private $controller; + protected $controller; /** * Prepare tests. @@ -55,92 +35,7 @@ class FrontControllerTest extends BaseTest { parent::setUp(); - $this->container = new Container(); - $this->request = Request::createFromEnvironment(Environment::mock()); - $this->response = new Response(); - $this->container['view'] = ViewFactory::create($this->container, $this->request); - $this->container['locale'] = new LocaleManager(); - $this->controller = new FrontController($this->container); - - $this->container['router']->map(['GET'], '/', [$this->controller, 'index']) - ->setName('index'); - $this->container['router']->map(['GET'], '/video', [$this->controller, 'info']) - ->setName('info'); - $this->container['router']->map(['GET'], '/extractors', [$this->controller, 'extractors']) - ->setName('extractors'); - $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'download']) - ->setName('download'); - $this->container['router']->map(['GET'], '/locale', [$this->controller, 'locale']) - ->setName('locale'); - } - - /** - * Run controller function with custom query parameters and return the result. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * - * @return Response HTTP response - */ - private function getRequestResult($request, array $params) - { - return $this->controller->$request( - $this->request->withQueryParams($params), - $this->response - ); - } - - /** - * Assert that calling controller function with these parameters returns a 200 HTTP response. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * - * @return void - */ - private function assertRequestIsOk($request, array $params = []) - { - $this->assertTrue($this->getRequestResult($request, $params)->isOk()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP redirect. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * - * @return void - */ - private function assertRequestIsRedirect($request, array $params = []) - { - $this->assertTrue($this->getRequestResult($request, $params)->isRedirect()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP 500 error. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * - * @return void - */ - private function assertRequestIsServerError($request, array $params = []) - { - $this->assertTrue($this->getRequestResult($request, $params)->isServerError()); - } - - /** - * Assert that calling controller function with these parameters returns an HTTP 400 error. - * - * @param string $request Controller function to call - * @param array $params Query parameters - * - * @return void - */ - private function assertRequestIsClientError($request, array $params = []) - { - $this->assertTrue($this->getRequestResult($request, $params)->isClientError()); } /** @@ -239,7 +134,10 @@ class FrontControllerTest extends BaseTest { Config::setOptions(['convert' => true]); - $this->assertRequestIsRedirect('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true]); + $this->assertRequestIsRedirect( + 'info', + ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'audio' => true] + ); } /** @@ -348,231 +246,6 @@ class FrontControllerTest extends BaseTest $this->assertTrue($result->isServerError()); } - /** - * Test the download() function without the URL parameter. - * - * @return void - */ - public function testDownloadWithoutUrl() - { - $this->assertRequestIsRedirect('download'); - } - - /** - * Test the download() function. - * - * @return void - */ - public function testDownload() - { - $this->assertRequestIsRedirect('download', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); - } - - /** - * Test the download() function with a specific format. - * - * @return void - */ - public function testDownloadWithFormat() - { - $this->assertRequestIsRedirect( - 'download', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'format' => 'worst'] - ); - } - - /** - * Test the download() function with streams enabled. - * - * @return void - */ - public function testDownloadWithStream() - { - Config::setOptions(['stream' => true]); - - $this->assertRequestIsOk( - 'download', - ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU'] - ); - } - - /** - * Test the download() function with an M3U stream. - * - * @return void - */ - public function testDownloadWithM3uStream() - { - if (getenv('CI')) { - $this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.'); - } - - Config::setOptions(['stream' => true]); - - $this->assertRequestIsOk( - 'download', - [ - 'url' => 'https://twitter.com/verge/status/813055465324056576/video/1', - 'format' => 'hls-2176', - ] - ); - } - - /** - * Test the download() function with an RTMP stream. - * - * @return void - */ - public function testDownloadWithRtmpStream() - { - $this->markTestIncomplete('We need to find another RTMP video.'); - - Config::setOptions(['stream' => true]); - - $this->assertRequestIsOk( - 'download', - ['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264'] - ); - } - - /** - * Test the download() function with a remuxed video. - * - * @return void - */ - public function testDownloadWithRemux() - { - Config::setOptions(['remux' => true]); - - $this->assertRequestIsOk( - 'download', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'bestvideo+bestaudio', - ] - ); - } - - /** - * Test the download() function with a remuxed video but remux disabled. - * - * @return void - */ - public function testDownloadWithRemuxDisabled() - { - $this->assertRequestIsServerError( - 'download', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'bestvideo+bestaudio', - ] - ); - } - - /** - * Test the download() function with a missing password. - * - * @return void - */ - public function testDownloadWithMissingPassword() - { - if (getenv('CI')) { - $this->markTestSkipped('Travis is blacklisted by Vimeo.'); - } - $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']); - } - - /** - * Test the download() function with an error. - * - * @return void - */ - public function testDownloadWithError() - { - $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']); - } - - /** - * Test the download() function with an video that returns an empty URL. - * This can be caused by trying to redirect to a playlist. - * - * @return void - */ - public function testDownloadWithEmptyUrl() - { - $this->assertRequestIsServerError( - 'download', - ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] - ); - } - - /** - * Test the download() function with a playlist stream. - * - * @return void - * @requires OS Linux - */ - public function testDownloadWithPlaylist() - { - Config::setOptions(['stream' => true]); - - $this->assertRequestIsOk( - 'download', - ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] - ); - } - - /** - * Test the download() function with an advanced conversion. - * - * @return void - */ - public function testDownloadWithAdvancedConversion() - { - Config::setOptions(['convertAdvanced' => true]); - - $this->assertRequestIsOk( - 'download', - [ - 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', - 'format' => 'best', - 'customConvert' => 'on', - 'customBitrate' => 32, - 'customFormat' => 'flv', - ] - ); - } - - /** - * Test the json() function without the URL parameter. - * - * @return void - */ - public function testJsonWithoutUrl() - { - $this->assertRequestIsClientError('json'); - } - - /** - * Test the json() function. - * - * @return void - */ - public function testJson() - { - $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); - } - - /** - * Test the json() function with an error. - * - * @return void - */ - public function testJsonWithError() - { - $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']); - } - /** * Test the locale() function. * diff --git a/tests/JsonControllerTest.php b/tests/JsonControllerTest.php new file mode 100644 index 0000000..05003f5 --- /dev/null +++ b/tests/JsonControllerTest.php @@ -0,0 +1,63 @@ +controller = new JsonController($this->container); + } + + /** + * Test the json() function. + * + * @return void + */ + public function testJson() + { + $this->assertRequestIsOk('json', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); + } + + /** + * Test the json() function with an error. + * + * @return void + */ + public function testJsonWithError() + { + $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']); + } + + /** + * Test the json() function without the URL parameter. + * + * @return void + */ + public function testJsonWithoutUrl() + { + $this->assertRequestIsClientError('json'); + } +}