diff --git a/.gitignore b/.gitignore index 0fb6d93..af93b59 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ vendor/ templates_c/ ffmpeg.tar.xz ffmpeg-*/ -alltube-release.zip +alltube-*.zip coverage/ bower_components/ config.yml diff --git a/.htaccess b/.htaccess index 29ed68b..8fc5b69 100644 --- a/.htaccess +++ b/.htaccess @@ -14,6 +14,10 @@ Addtype font/truetype .ttf FileETag None RewriteEngine On + +RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC] +RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L] + RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [QSA,L] diff --git a/Dockerfile b/Dockerfile index 74e03bb..5dbf4fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:apache +FROM php:5.6-apache RUN apt-get update RUN apt-get install -y libicu-dev xz-utils git zlib1g-dev python npm nodejs-legacy RUN docker-php-ext-install mbstring diff --git a/Gruntfile.js b/Gruntfile.js index 3dcbf99..1d05fad 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,6 +3,11 @@ module.exports = function (grunt) { 'use strict'; grunt.initConfig( { + githash: { + main: { + options: {} + } + }, uglify: { combine: { files: { @@ -52,7 +57,7 @@ module.exports = function (grunt) { compress: { release: { options: { - archive: 'alltube-release.zip' + archive: 'alltube-<%= githash.main.tag %>.zip' }, src: ['*.php', '!config.yml', 'dist/**', 'fonts/**', '.htaccess', 'img/**', 'js/**', 'LICENSE', 'README.md', 'robots.txt', 'sitemap.xml', 'templates/**', 'templates_c/', 'vendor/**', 'classes/**', 'controllers/**', 'bower_components/**', '!vendor/ffmpeg/**', '!vendor/bin/ffmpeg'] } @@ -60,6 +65,7 @@ module.exports = function (grunt) { } ); + grunt.loadNpmTasks('grunt-githash'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-watch'); @@ -70,5 +76,5 @@ module.exports = function (grunt) { grunt.registerTask('default', ['uglify', 'cssmin']); grunt.registerTask('lint', ['phpcs']); grunt.registerTask('test', ['phpunit']); - grunt.registerTask('release', ['default', 'compress']); + grunt.registerTask('release', ['default', 'githash', 'compress']); }; diff --git a/classes/Config.php b/classes/Config.php index 77cfa57..cd8dea1 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -31,10 +31,11 @@ class Config public $youtubedl = 'vendor/rg3/youtube-dl/youtube_dl/__main__.py'; public $python = '/usr/bin/python'; - public $params = array('--no-playlist', '--no-warnings', '-f best'); + public $params = array('--no-playlist', '--no-warnings', '-f best[protocol^=http]', '--playlist-end', 1); public $convert = false; public $avconv = 'vendor/bin/ffmpeg'; - public $curl_params = ''; + public $rtmpdump = 'vendor/bin/rtmpdump'; + public $curl_params = array(); /** * Config constructor diff --git a/composer.json b/composer.json index 86c5e22..1ea4b73 100644 --- a/composer.json +++ b/composer.json @@ -6,13 +6,15 @@ "type": "project", "require": { "smarty/smarty": "~3.1.29", - "rg3/youtube-dl": "2016.04.05", - "slim/slim": "~3.3.0", + "rg3/youtube-dl": "~2016.04.13", + "slim/slim": "3.x-dev", "mathmarques/smarty-view": "~1.1.0", "symfony/yaml": "~3.0.0", "symfony/process": "~3.0.0", - "ffmpeg/ffmpeg": "~2.8.2", - "rudloff/smarty-plugin-noscheme": "~0.1.0" + "ptachoire/process-builder-chain": "~1.2.0", + "ffmpeg/ffmpeg": "dev-release", + "rudloff/smarty-plugin-noscheme": "~0.1.0", + "rudloff/rtmpdump-bin": "~2.3" }, "require-dev": { "symfony/var-dumper": "~3.0.0" @@ -28,18 +30,18 @@ "type": "package", "package": { "name": "rg3/youtube-dl", - "version": "2016.04.05", + "version": "2016.04.13", "source": { "url": "https://github.com/rg3/youtube-dl.git", "type": "git", - "reference": "2016.04.05" + "reference": "9e285387260a019d7471c3bdbd52cc764c0e8700" } } }, { "type": "package", "package": { "name": "ffmpeg/ffmpeg", - "version": "2.8.4", + "version": "dev-release", "dist": { "url": "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz", "type": "xz" diff --git a/composer.lock b/composer.lock index 8a229b5..71dc2b8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "829ac07cae5bc43ef0ad3f5e5be618c5", - "content-hash": "a7131399bb03f78ba0dcfd8ab7e98d59", + "hash": "f753f2447bc606e2ab66785d88bd1189", + "content-hash": "cddbd63bbeaecc24145efac93485ae1f", "packages": [ { "name": "container-interop/container-interop", @@ -36,7 +36,7 @@ }, { "name": "ffmpeg/ffmpeg", - "version": "2.8.4", + "version": "dev-release", "dist": { "type": "xz", "url": "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz", @@ -226,16 +226,16 @@ }, { "name": "nikic/fast-route", - "version": "v0.6.0", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/nikic/FastRoute.git", - "reference": "31fa86924556b80735f98b294a7ffdfb26789f22" + "reference": "79843dce62ac52e9b628e73d5f1264cad10c65a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/31fa86924556b80735f98b294a7ffdfb26789f22", - "reference": "31fa86924556b80735f98b294a7ffdfb26789f22", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/79843dce62ac52e9b628e73d5f1264cad10c65a6", + "reference": "79843dce62ac52e9b628e73d5f1264cad10c65a6", "shasum": "" }, "require": { @@ -265,7 +265,7 @@ "router", "routing" ], - "time": "2015-06-18 19:15:47" + "time": "2016-04-18 11:33:20" }, { "name": "pimple/pimple", @@ -362,16 +362,80 @@ ], "time": "2015-05-04 20:22:00" }, + { + "name": "ptachoire/process-builder-chain", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/krichprollsch/process-builder-chain.git", + "reference": "465055dbcc3b5ef792a768df935571551de4781a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/krichprollsch/process-builder-chain/zipball/465055dbcc3b5ef792a768df935571551de4781a", + "reference": "465055dbcc3b5ef792a768df935571551de4781a", + "shasum": "" + }, + "require": { + "symfony/process": "~2.5 || ~3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Chain": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pierre Tachoire", + "email": "pierre.tachoire@gmail.com" + } + ], + "description": "Add ability to chain symfony processes", + "time": "2016-04-10 08:33:20" + }, { "name": "rg3/youtube-dl", - "version": "2016.04.05", + "version": "2016.04.13", "source": { "type": "git", "url": "https://github.com/rg3/youtube-dl.git", - "reference": "2016.04.05" + "reference": "9e285387260a019d7471c3bdbd52cc764c0e8700" }, "type": "library" }, + { + "name": "rudloff/rtmpdump-bin", + "version": "2.3", + "source": { + "type": "git", + "url": "https://github.com/Rudloff/rtmpdump-bin.git", + "reference": "133cdd80e3bab66593e88a5276158596383afd97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Rudloff/rtmpdump-bin/zipball/133cdd80e3bab66593e88a5276158596383afd97", + "reference": "133cdd80e3bab66593e88a5276158596383afd97", + "shasum": "" + }, + "require-dev": { + "rtmpdump/rtmpdump": "2.3" + }, + "bin": [ + "rtmpdump" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "description": "rtmpdump binary for Linux 64 bit", + "time": "2016-04-12 19:17:32" + }, { "name": "rudloff/smarty-plugin-noscheme", "version": "0.1.1", @@ -415,25 +479,28 @@ }, { "name": "slim/slim", - "version": "3.3.0", + "version": "3.x-dev", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "939f2e85d57508de9cff241d10091cd972f221c3" + "reference": "30cfe3c07dac28ec1129c0577e64b90ba11a54c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/939f2e85d57508de9cff241d10091cd972f221c3", - "reference": "939f2e85d57508de9cff241d10091cd972f221c3", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/30cfe3c07dac28ec1129c0577e64b90ba11a54c4", + "reference": "30cfe3c07dac28ec1129c0577e64b90ba11a54c4", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "nikic/fast-route": "^0.6", + "nikic/fast-route": "^1.0", "php": ">=5.5.0", "pimple/pimple": "^3.0", "psr/http-message": "^1.0" }, + "provide": { + "psr/http-message-implementation": "1.0" + }, "require-dev": { "phpunit/phpunit": "^4.0", "squizlabs/php_codesniffer": "^2.5" @@ -478,7 +545,7 @@ "micro", "router" ], - "time": "2016-03-10 21:37:40" + "time": "2016-05-26 08:20:33" }, { "name": "smarty/smarty", @@ -537,16 +604,16 @@ }, { "name": "symfony/process", - "version": "v3.0.4", + "version": "v3.0.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "e6f1f98bbd355d209a992bfff45e7edfbd4a0776" + "reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/e6f1f98bbd355d209a992bfff45e7edfbd4a0776", - "reference": "e6f1f98bbd355d209a992bfff45e7edfbd4a0776", + "url": "https://api.github.com/repos/symfony/process/zipball/53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb", + "reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb", "shasum": "" }, "require": { @@ -582,11 +649,11 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-03-30 10:41:14" + "time": "2016-04-14 15:30:28" }, { "name": "symfony/yaml", - "version": "v3.0.4", + "version": "v3.0.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -637,16 +704,16 @@ "packages-dev": [ { "name": "symfony/polyfill-mbstring", - "version": "v1.1.1", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "1289d16209491b584839022f29257ad859b8532d" + "reference": "dff51f72b0706335131b00a7f49606168c582594" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", - "reference": "1289d16209491b584839022f29257ad859b8532d", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", "shasum": "" }, "require": { @@ -658,7 +725,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -692,20 +759,20 @@ "portable", "shim" ], - "time": "2016-01-20 09:13:37" + "time": "2016-05-18 14:26:46" }, { "name": "symfony/var-dumper", - "version": "v3.0.4", + "version": "v3.0.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "3841ed86527d18ee2c35fe4afb1b2fc60f8fae79" + "reference": "0e918c269093ba4c77fca14e9424fa74ed16f1a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3841ed86527d18ee2c35fe4afb1b2fc60f8fae79", - "reference": "3841ed86527d18ee2c35fe4afb1b2fc60f8fae79", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e918c269093ba4c77fca14e9424fa74ed16f1a6", + "reference": "0e918c269093ba4c77fca14e9424fa74ed16f1a6", "shasum": "" }, "require": { @@ -755,12 +822,15 @@ "debug", "dump" ], - "time": "2016-03-10 10:34:12" + "time": "2016-04-25 11:17:47" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "slim/slim": 20, + "ffmpeg/ffmpeg": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": [], diff --git a/composer.phar b/composer.phar deleted file mode 100755 index 185064f..0000000 Binary files a/composer.phar and /dev/null differ diff --git a/config.example.yml b/config.example.yml index 23207ea..9f66ab7 100644 --- a/config.example.yml +++ b/config.example.yml @@ -4,6 +4,9 @@ params: - --no-playlist - --no-warnings - -f best[protocol^=http] + - --playlist-end + - 1 curl_params: convert: false avconv: vendor/bin/ffmpeg +rtmpdump: vendor/bin/rtmpdump diff --git a/controllers/FrontController.php b/controllers/FrontController.php index f99d19d..1c8939a 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -14,6 +14,9 @@ namespace Alltube\Controller; use Alltube\VideoDownload; use Alltube\Config; +use Symfony\Component\Process\ProcessBuilder; +use Chain\Chain; +use Slim\Http\Stream; /** * Main controller @@ -49,7 +52,8 @@ class FrontController $response, 'head.tpl', array( - 'class'=>'index' + 'class'=>'index', + 'description'=>'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.' ) ); $container->view->render( @@ -81,7 +85,10 @@ class FrontController $response, 'head.tpl', array( - 'class'=>'extractors' + 'class'=>'extractors', + 'title'=>'Supported websites', + 'description' + =>'List of all supported websites from which Alltube Download can extract video or audio files' ) ); $container->view->render($response, 'header.tpl'); @@ -112,104 +119,134 @@ class FrontController if (isset($params["url"])) { if (isset($params['audio'])) { try { - try { - $url = $this->download->getURL($params["url"], 'bestaudio[protocol^=http]'); - return $response->withRedirect($url); - } catch (\Exception $e) { - $video = $this->download->getJSON($params["url"]); - - //Vimeo needs a correct user-agent - ini_set( - 'user_agent', - $video->http_headers->{'User-Agent'} - ); - if (parse_url($video->url, PHP_URL_SCHEME) == 'rtmp') { - ob_end_flush(); - header( - 'Content-Disposition: attachment; filename="'. - html_entity_decode( - pathinfo( - $video->_filename, - PATHINFO_FILENAME - ).'.mp3', - ENT_COMPAT, - 'ISO-8859-1' - ).'"' - ); - header("Content-Type: audio/mpeg"); - passthru( - '/usr/bin/rtmpdump -q -r '.escapeshellarg($video->url). - ' | '.$this->config->avconv. - ' -v quiet -i - -f mp3 -vn pipe:1' - ); - exit; - } else { - ob_end_flush(); - header( - 'Content-Disposition: attachment; filename="'. - html_entity_decode( - pathinfo( - $video->_filename, - PATHINFO_FILENAME - ).'.mp3', - ENT_COMPAT, - 'ISO-8859-1' - ).'"' - ); - header("Content-Type: audio/mpeg"); - passthru( - 'curl '.$this->config->curl_params. - ' --user-agent '.escapeshellarg($video->http_headers->{'User-Agent'}). - ' '.escapeshellarg($video->url). - ' | '.$this->config->avconv. - ' -v quiet -i - -f mp3 -vn pipe:1' - ); - exit; - } - } + $url = $this->download->getURL($params["url"], 'mp3[protocol^=http]'); + return $response->withRedirect($url); } catch (\Exception $e) { - $error = $e->getMessage(); + $video = $this->download->getJSON($params["url"], 'bestaudio/best'); + + $avconvProc = ProcessBuilder::create( + array( + $this->config->avconv, + '-v', 'quiet', + '-i', '-', + '-f', 'mp3', + '-vn', + 'pipe:1' + ) + ); + + //Vimeo needs a correct user-agent + ini_set( + 'user_agent', + $video->http_headers->{'User-Agent'} + ); + + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + html_entity_decode( + pathinfo( + $video->_filename, + PATHINFO_FILENAME + ).'.mp3', + ENT_COMPAT, + 'ISO-8859-1' + ).'"' + ); + $response = $response->withHeader('Content-Type', 'audio/mpeg'); + + if (parse_url($video->url, PHP_URL_SCHEME) == 'rtmp') { + $builder = new ProcessBuilder( + array( + $this->config->rtmpdump, + '-q', + '-r', + $video->url, + '--pageUrl', $video->webpage_url + ) + ); + if (isset($video->player_url)) { + $builder->add('--swfVfy'); + $builder->add($video->player_url); + } + if (isset($video->flash_version)) { + $builder->add('--flashVer'); + $builder->add($video->flash_version); + } + if (isset($video->play_path)) { + $builder->add('--playpath'); + $builder->add($video->play_path); + } + foreach ($video->rtmp_conn as $conn) { + $builder->add('--conn'); + $builder->add($conn); + } + $chain = new Chain($builder->getProcess()); + $chain->add('|', $avconvProc); + } else { + $chain = new Chain( + ProcessBuilder::create( + array_merge( + array( + 'curl', + '--silent', + '--user-agent', $video->http_headers->{'User-Agent'}, + $video->url + ), + $this->config->curl_params + ) + ) + ); + $chain->add('|', $avconvProc); + } + if ($request->isGet()) { + $response = $response->withBody(new Stream(popen($chain->getProcess()->getCommandLine(), 'r'))); + } + return $response; } } else { - try { - $video = $this->download->getJSON($params["url"]); - $container->view->render( - $response, - 'head.tpl', - array( - 'class'=>'video' - ) - ); - $container->view->render( - $response, - 'video.tpl', - array( - 'video'=>$video - ) - ); - $container->view->render($response, 'footer.tpl'); - } catch (\Exception $e) { - $error = $e->getMessage(); - } + $video = $this->download->getJSON($params["url"]); + $container->view->render( + $response, + 'head.tpl', + array( + 'class'=>'video', + 'title'=>$video->title, + 'description'=>'Download "'.$video->title.'" from '.$video->extractor_key + ) + ); + $container->view->render( + $response, + 'video.tpl', + array( + 'video'=>$video + ) + ); + $container->view->render($response, 'footer.tpl'); } } - if (isset($error)) { - $container->view->render( - $response, - 'head.tpl', - array( - 'class'=>'video' - ) - ); - $container->view->render( - $response, - 'error.tpl', - array( - 'errors'=>$error - ) - ); - $container->view->render($response, 'footer.tpl'); - } + } + + public function error($request, $response, $exception) + { + global $container; + $container->view->render( + $response, + 'head.tpl', + array( + 'class'=>'video', + 'title'=>'Error' + ) + ); + $container->view->render( + $response, + 'error.tpl', + array( + 'errors'=>$exception->getMessage() + ) + ); + $container->view->render($response, 'footer.tpl'); + return $response->withStatus(500); } /** diff --git a/css/style.css b/css/style.css index 028928e..410c2e4 100644 --- a/css/style.css +++ b/css/style.css @@ -570,6 +570,7 @@ h1 { } @media (max-width: 640px) { + .formats, .thumb { width:90%; } diff --git a/error.html b/error.html index 81b5d00..b3ac86c 100644 --- a/error.html +++ b/error.html @@ -1,5 +1,5 @@ - + @@ -9,7 +9,7 @@
-

An error occurred in the application and your page could not be served. Please try again in a few moments.
diff --git a/index.php b/index.php index a934ac8..9a33cd7 100644 --- a/index.php +++ b/index.php @@ -30,6 +30,8 @@ $container['view'] = function ($c) { $controller = new FrontController(); +$container['errorHandler'] = array($controller, 'error'); + $app->get( '/', array($controller, 'index') diff --git a/maintenance.html b/maintenance.html index f7ba7a5..cae4766 100644 --- a/maintenance.html +++ b/maintenance.html @@ -1,5 +1,5 @@ - + @@ -9,7 +9,7 @@
-

This application is undergoing maintenance right now. Please check back later.
diff --git a/package.json b/package.json index d6213e8..5e4480d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "grunt-phpcs": "~0.4.0", "grunt-phpunit": "~0.3.6", "grunt-contrib-compress": "~1.2.0", - "bower": "~1.7.1" + "bower": "~1.7.1", + "grunt-githash": "~0.1.3" }, "repository": { "type": "git", diff --git a/templates/footer.tpl b/templates/footer.tpl index 8dcf4dd..cdbb327 100644 --- a/templates/footer.tpl +++ b/templates/footer.tpl @@ -1,10 +1,10 @@