diff --git a/classes/Config.php b/classes/Config.php index 0c1aa8c..77cfa57 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -31,7 +31,7 @@ class Config public $youtubedl = 'vendor/rg3/youtube-dl/youtube_dl/__main__.py'; public $python = '/usr/bin/python'; - public $params = '--no-playlist --no-warnings -f best'; + public $params = array('--no-playlist', '--no-warnings', '-f best'); public $convert = false; public $avconv = 'vendor/bin/ffmpeg'; public $curl_params = ''; diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index ea0ca2c..8643207 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -13,6 +13,7 @@ namespace Alltube; use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; /** * Main class @@ -27,19 +28,31 @@ use Symfony\Component\Process\Process; * */ class VideoDownload { + public function __construct() + { + $this->config = Config::getInstance(); + $this->procBuilder = new ProcessBuilder(); + $this->procBuilder->setPrefix( + array_merge( + array($this->config->python, $this->config->youtubedl), + $this->config->params + ) + ); + } + /** * Get the user agent used youtube-dl * * @return string UA * */ - public static function getUA() + public function getUA() { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--dump-user-agent' + ) ); - $process = new Process($cmd.' --dump-user-agent'); + $process = $this->procBuilder->getProcess(); $process->run(); return trim($process->getOutput()); } @@ -49,14 +62,14 @@ class VideoDownload * * @return array Extractors * */ - public static function listExtractors() + public function listExtractors() { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--list-extractors' + ) ); - $process = new Process($cmd.' --list-extractors'); + $process = $this->procBuilder->getProcess(); $process->run(); return explode(PHP_EOL, $process->getOutput()); } @@ -69,18 +82,18 @@ class VideoDownload * * @return string Filename * */ - public static function getFilename($url, $format = null) + public function getFilename($url, $format = null) { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--get-filename', + $url + ) ); if (isset($format)) { - $cmd .= ' -f '.escapeshellarg($format); + $this->procBuilder->add('-f '.$format); } - $cmd .=' --get-filename '.escapeshellarg($url)." 2>&1"; - $process = new Process($cmd); + $process = $this->procBuilder->getProcess(); $process->run(); return trim($process->getOutput()); } @@ -91,23 +104,23 @@ class VideoDownload * @param string $url URL of page * @param string $format Format to use for the video * - * @return string JSON + * @return object Decoded JSON * */ - public static function getJSON($url, $format = null) + public function getJSON($url, $format = null) { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--dump-json', + $url + ) ); if (isset($format)) { - $cmd .= ' -f '.escapeshellarg($format); + $this->procBuilder->add('-f '.$format); } - $cmd .=' --dump-json '.escapeshellarg($url)." 2>&1"; - $process = new Process($cmd); + $process = $this->procBuilder->getProcess(); $process->run(); if (!$process->isSuccessful()) { - throw new \Exception($process->getOutput()); + throw new \Exception($process->getErrorOutput()); } else { return json_decode($process->getOutput()); } @@ -121,23 +134,23 @@ class VideoDownload * * @return string URL of video * */ - public static function getURL($url, $format = null) + public function getURL($url, $format = null) { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--get-url', + $url + ) ); if (isset($format)) { - $cmd .= ' -f '.escapeshellarg($format); + $this->procBuilder->add('-f '.$format); } - $cmd .=' -g '.escapeshellarg($url)." 2>&1"; - $process = new Process($cmd); + $process = $this->procBuilder->getProcess(); $process->run(); if (!$process->isSuccessful()) { - throw new \Exception($process->getOutput()); + throw new \Exception($process->getErrorOutput()); } else { - return array('success'=>true, 'url'=>$process->getOutput()); + return $process->getOutput(); } } diff --git a/config.example.yml b/config.example.yml index 0734d4d..a9b905e 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,6 +1,9 @@ youtubedl: vendor/rg3/youtube-dl/youtube_dl/__main__.py python: /usr/bin/python -params: --no-playlist --no-warnings -f best +params: + - --no-playlist + - --no-warnings + - -f best curl_params: convert: false avconv: vendor/bin/ffmpeg diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 6e734cd..aba76f5 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -28,6 +28,11 @@ use Alltube\Config; * */ class FrontController { + public function __construct() + { + $this->config = Config::getInstance(); + $this->download = new VideoDownload(); + } /** * Display index page @@ -37,10 +42,9 @@ class FrontController * * @return void */ - public static function index($request, $response) + public function index($request, $response) { global $container; - $config = Config::getInstance(); $container->view->render( $response, 'head.tpl', @@ -56,7 +60,7 @@ class FrontController $response, 'index.tpl', array( - 'convert'=>$config->convert + 'convert'=>$this->config->convert ) ); $container->view->render($response, 'footer.tpl'); @@ -70,7 +74,7 @@ class FrontController * * @return void */ - public static function extractors($request, $response) + public function extractors($request, $response) { global $container; $container->view->render( @@ -86,7 +90,7 @@ class FrontController $response, 'extractors.tpl', array( - 'extractors'=>VideoDownload::listExtractors() + 'extractors'=>$this->download->listExtractors() ) ); $container->view->render($response, 'footer.tpl'); @@ -100,18 +104,18 @@ class FrontController * * @return void */ - public static function video($request, $response) + public function video($request, $response) { global $container; $params = $request->getQueryParams(); - $config = Config::getInstance(); + $this->config = Config::getInstance(); if (isset($params["url"])) { if (isset($params['audio'])) { try { - $video = VideoDownload::getJSON($params["url"]); + $video = $this->download->getJSON($params["url"]); //Vimeo needs a correct user-agent - $UA = VideoDownload::getUA(); + $UA = $this->download->getUA(); ini_set( 'user_agent', $UA @@ -123,7 +127,7 @@ class FrontController 'Content-Disposition: attachment; filename="'. html_entity_decode( pathinfo( - VideoDownload::getFilename( + $this->download->getFilename( $video->webpage_url ), PATHINFO_FILENAME @@ -135,7 +139,7 @@ class FrontController header("Content-Type: audio/mpeg"); passthru( '/usr/bin/rtmpdump -q -r '.escapeshellarg($video->url). - ' | '.$config->avconv. + ' | '.$this->config->avconv. ' -v quiet -i - -f mp3 -vn pipe:1' ); exit; @@ -145,7 +149,7 @@ class FrontController 'Content-Disposition: attachment; filename="'. html_entity_decode( pathinfo( - VideoDownload::getFilename( + $this->download->getFilename( $video->webpage_url ), PATHINFO_FILENAME @@ -156,10 +160,10 @@ class FrontController ); header("Content-Type: audio/mpeg"); passthru( - 'curl '.$config->curl_params. + 'curl '.$this->config->curl_params. ' --user-agent '.escapeshellarg($UA). ' '.escapeshellarg($video->url). - ' | '.$config->avconv. + ' | '.$this->config->avconv. ' -v quiet -i - -f mp3 -vn pipe:1' ); exit; @@ -169,7 +173,7 @@ class FrontController } } else { try { - $video = VideoDownload::getJSON($params["url"]); + $video = $this->download->getJSON($params["url"]); $container->view->render( $response, 'head.tpl', @@ -217,14 +221,14 @@ class FrontController * * @return void */ - public static function redirect($request, $response) + public function redirect($request, $response) { global $app; $params = $request->getQueryParams(); if (isset($params["url"])) { try { $format = isset($params["format"]) ? $params["format"] : 'best'; - $video = VideoDownload::getJSON($params["url"], $format); + $video = $this->download->getJSON($params["url"], $format); $client = new \GuzzleHttp\Client(); $stream = $client->request('GET', $video->url, array('stream'=>true)); $response = $response->withHeader('Content-Disposition', 'inline; filename="'.$video->_filename.'"'); @@ -249,13 +253,13 @@ class FrontController * * @return void */ - public static function json($request, $response) + public function json($request, $response) { global $app; $params = $request->getQueryParams(); if (isset($params["url"])) { try { - $video = VideoDownload::getJSON($params["url"]); + $video = $this->download->getJSON($params["url"]); return $response->withJson($video); } catch (\Exception $e) { return $response->withJson( diff --git a/index.php b/index.php index c26d743..de9387b 100644 --- a/index.php +++ b/index.php @@ -15,6 +15,7 @@ require_once __DIR__.'/vendor/autoload.php'; require_once __DIR__.'/vendor/rudloff/smarty-plugin-noscheme/modifier.noscheme.php'; use Alltube\VideoDownload; +use Alltube\Controller\FrontController; $app = new \Slim\App(); $container = $app->getContainer(); @@ -28,24 +29,26 @@ $container['view'] = function ($c) { return $view; }; +$controller = new FrontController(); + $app->get( '/', - array('Alltube\Controller\FrontController', 'index') + array($controller, 'index') ); $app->get( '/extractors', - array('Alltube\Controller\FrontController', 'extractors') + array($controller, 'extractors') )->setName('extractors'); $app->get( '/video', - array('Alltube\Controller\FrontController', 'video') + array($controller, 'video') )->setName('video'); $app->get( '/redirect', - array('Alltube\Controller\FrontController', 'redirect') + array($controller, 'redirect') )->setName('redirect'); $app->get( '/json', - array('Alltube\Controller\FrontController', 'json') + array($controller, 'json') ); $app->run(); diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index 9f0c7b4..d137735 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -27,6 +27,11 @@ use Alltube\VideoDownload; * */ class VideoDownloadTest extends \PHPUnit_Framework_TestCase { + protected function setUp() + { + $this->download = new VideoDownload(); + } + /** * Test getUA function * @@ -34,7 +39,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testGetUA() { - $this->assertStringStartsWith('Mozilla/', VideoDownload::getUA()); + $this->assertStringStartsWith('Mozilla/', $this->download->getUA()); } /** @@ -44,7 +49,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testListExtractors() { - $extractors = VideoDownload::listExtractors(); + $extractors = $this->download->listExtractors(); $this->assertContains('youtube', $extractors); } @@ -57,11 +62,10 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase * @return void * @dataProvider urlProvider */ - public function testGetURL($url, $format) + public function testGetURL($url, $format, $filename, $domain) { - $videoURL = VideoDownload::getURL($url, $format); - $this->assertArrayHasKey('success', $videoURL); - $this->assertArrayHasKey('url', $videoURL); + $videoURL = $this->download->getURL($url, $format); + $this->assertContains($domain, $videoURL); } /** @@ -75,7 +79,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testGetURLError($url) { - $videoURL = VideoDownload::getURL($url); + $this->download->getURL($url); } /** @@ -88,16 +92,19 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase return array( array( 'https://www.youtube.com/watch?v=M7IpKCZ47pU', null, - "It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU.mp4" + "It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU.mp4", + 'googlevideo.com' ), array( 'https://www.youtube.com/watch?v=RJJ6FCAXvKg', 22, "'Heart Attack' - Demi Lovato ". - "(Sam Tsui & Against The Current)-RJJ6FCAXvKg.mp4" + "(Sam Tsui & Against The Current)-RJJ6FCAXvKg.mp4", + 'googlevideo.com' ), array( 'https://vimeo.com/24195442', null, - "Carving the Mountains-24195442.mp4" + "Carving the Mountains-24195442.mp4", + 'vimeocdn.com' ), ); } @@ -126,7 +133,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testGetFilename($url, $format, $result) { - $filename = VideoDownload::getFilename($url, $format); + $filename = $this->download->getFilename($url, $format); $this->assertEquals($filename, $result); } @@ -141,7 +148,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testGetJSON($url, $format) { - $info = VideoDownload::getJSON($url, $format); + $info = $this->download->getJSON($url, $format); $this->assertObjectHasAttribute('webpage_url', $info); $this->assertObjectHasAttribute('url', $info); $this->assertObjectHasAttribute('ext', $info); @@ -161,6 +168,6 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase */ public function testGetJSONError($url) { - $videoURL = VideoDownload::getJSON($url); + $videoURL = $this->download->getJSON($url); } }