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 83d9799..8fc5b69 100644 --- a/.htaccess +++ b/.htaccess @@ -1,4 +1,6 @@ AddType application/x-web-app-manifest+json .webapp +Addtype font/truetype .ttf + ExpiresActive On ExpiresByType application/javascript "access plus 1 week" @@ -6,11 +8,20 @@ AddType application/x-web-app-manifest+json .webapp ExpiresByType image/png "access plus 1 week" ExpiresByType image/jpeg "access plus 1 week" ExpiresByType image/svg+xml "access plus 1 week" + ExpiresByType font/truetype "access plus 1 week" + 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] Redirect permanent /api.php /video Redirect permanent /extractors.php /extractors + +AddOutputFilterByType DEFLATE text/css text/html application/javascript 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 9998185..e18273a 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: { @@ -28,12 +33,17 @@ module.exports = function (grunt) { } }, phpcs: { + options: { + standard: 'PSR2' + }, php: { src: ['*.php', 'classes/*.php', 'controllers/*.php'] }, tests: { src: ['tests/*.php'] - }, + } + }, + jslint: { js: { src: ['js/*.js'] }, @@ -49,7 +59,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'] } @@ -57,15 +67,17 @@ module.exports = function (grunt) { } ); + grunt.loadNpmTasks('grunt-githash'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-phpcs'); grunt.loadNpmTasks('grunt-phpunit'); grunt.loadNpmTasks('grunt-contrib-compress'); + grunt.loadNpmTasks('grunt-jslint'); grunt.registerTask('default', ['uglify', 'cssmin']); - grunt.registerTask('lint', ['phpcs']); + grunt.registerTask('lint', ['phpcs', 'jslint']); grunt.registerTask('test', ['phpunit']); - grunt.registerTask('release', ['default', 'compress']); + grunt.registerTask('release', ['default', 'githash', 'compress']); }; diff --git a/README.md b/README.md index 32071af..d1c767d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,13 @@ HTML GUI for youtube-dl (http://alltubedownload.net/) ![Screenshot](img/screenshot.png "Alltube GUI screenshot") ##Setup + +### From a release package +You can download the latest release package [here](https://github.com/Rudloff/alltube/releases). + +You just have to unzip it on your server and it should be ready to use. + +### From Git In order to get AllTube working, you need to use [npm](https://www.npmjs.com/) and [Composer](https://getcomposer.org/): npm install @@ -27,6 +34,61 @@ If you want to use a custom config, you need to create a config file: cp config.example.yml config.yml +##Web server configuration +###Apache +You will need the following modules: + +* mod_mime +* mod_rewrite + +###Nginx +Here is an exemple Nginx configuration: + + server { + server_name localhost; + listen 443 ssl; + + root /var/www/path/to/alltube; + index index.php; + + access_log /var/log/nginx/alltube.access.log; + error_log /var/log/nginx/alltube.error.log; + + types { + text/html html htm shtml; + text/css css; + text/xml xml; + application/x-web-app-manifest+json webapp; + } + + # Deny access to dotfiles + location ~ /\. { + deny all; + } + + location / { + try_files $uri /index.php?$args; + } + + location ~ \.php$ { + try_files $uri /index.php?$args; + + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + fastcgi_pass unix:/var/run/php5-fpm.sock; + fastcgi_index index.php; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_intercept_errors off; + + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + + include fastcgi_params; + } + } + ##License This software is available under the [GNU General Public License](http://www.gnu.org/licenses/gpl.html). @@ -34,11 +96,11 @@ This software is available under the [GNU General Public License](http://www.gnu __Please use a different name and logo if you run it on a public server.__ ##Other dependencies -You need [avconv](https://libav.org/avconv.html) and [rtmpdump](http://rtmpdump.mplayerhq.hu/) in order to enable conversions. +You need [avconv](https://libav.org/avconv.html), [rtmpdump](http://rtmpdump.mplayerhq.hu/) and [curl](https://curl.haxx.se/) in order to enable conversions. If you don't want to enable conversions, you can disable it in *config.yml*. On Debian-based systems: - sudo apt-get install libav-tools rtmpdump + sudo apt-get install libav-tools rtmpdump curl 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). diff --git a/classes/Config.php b/classes/Config.php index 2e2bf11..cd8dea1 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -11,7 +11,9 @@ * @link http://rudloff.pro * */ namespace Alltube; + use Symfony\Component\Yaml\Yaml; + /** * Class to manage config parameters * @@ -23,16 +25,17 @@ use Symfony\Component\Yaml\Yaml; * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ -Class Config +class Config { - private static $_instance; + private static $instance; 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[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 @@ -43,7 +46,7 @@ Class Config if (is_file($yamlfile)) { $yaml = Yaml::parse(file_get_contents($yamlfile)); if (isset($yaml) && is_array($yaml)) { - foreach ($yaml as $param=>$value) { + foreach ($yaml as $param => $value) { if (isset($this->$param)) { $this->$param = $value; } @@ -62,9 +65,9 @@ Class Config */ public static function getInstance() { - if (is_null(self::$_instance)) { - self::$_instance = new Config(); + if (is_null(self::$instance)) { + self::$instance = new Config(); } - return self::$_instance; + return self::$instance; } } diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 9e2b435..22033cc 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -11,6 +11,10 @@ * @link http://rudloff.pro * */ namespace Alltube; + +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + /** * Main class * @@ -22,25 +26,18 @@ namespace Alltube; * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ -Class VideoDownload +class VideoDownload { - /** - * Get the user agent used youtube-dl - * - * @return string UA - * */ - static function getUA() + public function __construct() { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->config = Config::getInstance(); + $this->procBuilder = new ProcessBuilder(); + $this->procBuilder->setPrefix( + array_merge( + array($this->config->python, $this->config->youtubedl), + $this->config->params + ) ); - exec( - $cmd.' --dump-user-agent', - $version - ); - return $version[0]; } /** @@ -48,44 +45,16 @@ Class VideoDownload * * @return array Extractors * */ - static function listExtractors() + public function listExtractors() { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params + $this->procBuilder->setArguments( + array( + '--list-extractors' + ) ); - exec( - $cmd.' --list-extractors', - $extractors - ); - return $extractors; - } - - /** - * Get filename of video - * - * @param string $url URL of page - * @param string $format Format to use for the video - * - * @return string Filename - * */ - static function getFilename($url, $format=null) - { - $config = Config::getInstance(); - $cmd = escapeshellcmd( - $config->python.' '.escapeshellarg($config->youtubedl). - ' '.$config->params - ); - if (isset($format)) { - $cmd .= ' -f '.escapeshellarg($format); - } - $cmd .=' --get-filename '.escapeshellarg($url)." 2>&1"; - exec( - $cmd, - $filename - ); - return end($filename); + $process = $this->procBuilder->getProcess(); + $process->run(); + return explode(PHP_EOL, $process->getOutput()); } /** @@ -94,26 +63,25 @@ Class VideoDownload * @param string $url URL of page * @param string $format Format to use for the video * - * @return string JSON + * @return object Decoded JSON * */ - 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"; - exec( - $cmd, $result, $code - ); - if ($code>0) { - throw new \Exception(implode(PHP_EOL, $result)); + $process = $this->procBuilder->getProcess(); + $process->run(); + if (!$process->isSuccessful()) { + throw new \Exception($process->getErrorOutput()); } else { - return json_decode($result[0]); + return json_decode($process->getOutput()); } } @@ -125,24 +93,23 @@ Class VideoDownload * * @return string URL of video * */ - 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"; - exec( - $cmd, $result, $code - ); - if ($code>0) { - throw new \Exception(implode(PHP_EOL, $result)); + $process = $this->procBuilder->getProcess(); + $process->run(); + if (!$process->isSuccessful()) { + throw new \Exception($process->getErrorOutput()); } else { - return array('success'=>true, 'url'=>end($result)); + return $process->getOutput(); } } diff --git a/composer.json b/composer.json index dc5d1cf..56be992 100644 --- a/composer.json +++ b/composer.json @@ -6,15 +6,18 @@ "type": "project", "require": { "smarty/smarty": "~3.1.29", - "rg3/youtube-dl": "2016.03.14", - "slim/slim": "~2.6.2", - "slim/views": "~0.1.3", + "rg3/youtube-dl": "~2016.07.26", + "slim/slim": "~3.5.0", + "mathmarques/smarty-view": "~1.1.0", + "symfony/yaml": "~3.1.0", + "symfony/process": "~3.1.0", + "ptachoire/process-builder-chain": "~1.2.0", + "ffmpeg/ffmpeg": "dev-release", "rudloff/smarty-plugin-noscheme": "~0.1.0", - "symfony/yaml": "~3.0.0", - "ffmpeg/ffmpeg": "~2.8.2" + "rudloff/rtmpdump-bin": "~2.3" }, "require-dev": { - "symfony/var-dumper": "~3.0.0" + "symfony/var-dumper": "~3.1.0" }, "extra": { "paas": { @@ -27,18 +30,18 @@ "type": "package", "package": { "name": "rg3/youtube-dl", - "version": "2016.03.14", + "version": "2016.07.26", "source": { "url": "https://github.com/rg3/youtube-dl.git", "type": "git", - "reference": "2016.03.14" + "reference": "2016.07.26" } } }, { "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 d4fa979..17a3e32 100644 --- a/composer.lock +++ b/composer.lock @@ -4,12 +4,39 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d4b9e76dbda3af97316a38bfe163ac00", - "content-hash": "6eb27104cc39af34f798d35fb3f381ac", + "hash": "2afe3e4c1f053ce8a1dc13887ea3aa8c", + "content-hash": "2c86697f5f04b91631cd33dbedce3179", "packages": [ + { + "name": "container-interop/container-interop", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "time": "2014-12-30 15:22:37" + }, { "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", @@ -85,16 +112,16 @@ }, { "name": "league/uri", - "version": "4.1.0", + "version": "4.1.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "a4f0ea3323745214c955af2f6451d7743f30a076" + "reference": "0cbce9fe7d9808690ebda67b110ad96bcae9daee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/a4f0ea3323745214c955af2f6451d7743f30a076", - "reference": "a4f0ea3323745214c955af2f6451d7743f30a076", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/0cbce9fe7d9808690ebda67b110ad96bcae9daee", + "reference": "0cbce9fe7d9808690ebda67b110ad96bcae9daee", "shasum": "" }, "require": { @@ -145,7 +172,146 @@ "url", "ws" ], - "time": "2016-02-18 14:46:01" + "time": "2016-03-24 08:38:29" + }, + { + "name": "mathmarques/smarty-view", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/mathmarques/Smarty-View.git", + "reference": "66f4d28c564c0363eda18d3f5b85a4241b1c4ad1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mathmarques/Smarty-View/zipball/66f4d28c564c0363eda18d3f5b85a4241b1c4ad1", + "reference": "66f4d28c564c0363eda18d3f5b85a4241b1c4ad1", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "slim/slim": "^3.0", + "smarty/smarty": "~3.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Views\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matheus Marques", + "email": "matheusocmarques@gmail.com", + "homepage": "http://matheusmarques.com" + } + ], + "description": "Slim Framework 3 view helper built on top of the Smarty templating component", + "keywords": [ + "framework", + "slim", + "slim 3", + "smarty", + "template", + "view" + ], + "time": "2016-03-31 00:41:59" + }, + { + "name": "nikic/fast-route", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/8ea928195fa9b907f0d6e48312d323c1a13cc2af", + "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "time": "2016-06-12 19:08:51" + }, + { + "name": "pimple/pimple", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2015-09-11 15:10:35" }, { "name": "psr/http-message", @@ -196,37 +362,106 @@ ], "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.03.14", + "version": "2016.07.26", "source": { "type": "git", "url": "https://github.com/rg3/youtube-dl.git", - "reference": "2016.03.14" + "reference": "2016.07.26" }, "type": "library" }, { - "name": "rudloff/smarty-plugin-noscheme", - "version": "0.1.0", + "name": "rudloff/rtmpdump-bin", + "version": "2.3", "source": { "type": "git", - "url": "https://github.com/Rudloff/smarty-plugin-noscheme.git", - "reference": "537bcb2f7576252af70d8f9f817bfe050d873072" + "url": "https://github.com/Rudloff/rtmpdump-bin.git", + "reference": "133cdd80e3bab66593e88a5276158596383afd97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Rudloff/smarty-plugin-noscheme/zipball/537bcb2f7576252af70d8f9f817bfe050d873072", - "reference": "537bcb2f7576252af70d8f9f817bfe050d873072", + "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", + "source": { + "type": "git", + "url": "https://github.com/Rudloff/smarty-plugin-noscheme.git", + "reference": "7b64350bd255690e44db497e50bb5edc5e87d5e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Rudloff/smarty-plugin-noscheme/zipball/7b64350bd255690e44db497e50bb5edc5e87d5e6", + "reference": "7b64350bd255690e44db497e50bb5edc5e87d5e6", "shasum": "" }, "require": { - "league/uri": "~4.0" + "league/uri": "~4.1.1" }, "require-dev": { - "symfony/var-dumper": "~2.7.6" + "symfony/var-dumper": "~3.0.1" }, "type": "library", + "autoload": { + "files": [ + "modifier.noscheme.php" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-3.0" @@ -240,80 +475,40 @@ } ], "description": "Smarty modifier that removes the scheme in URLs", - "time": "2015-10-31 10:25:47" + "time": "2016-04-09 00:40:13" }, { "name": "slim/slim", - "version": "2.6.2", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "20a02782f76830b67ae56a5c08eb1f563c351a37" + "reference": "184352bc1913d7ba552ab4131d62f4730ddb0893" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/20a02782f76830b67ae56a5c08eb1f563c351a37", - "reference": "20a02782f76830b67ae56a5c08eb1f563c351a37", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/184352bc1913d7ba552ab4131d62f4730ddb0893", + "reference": "184352bc1913d7ba552ab4131d62f4730ddb0893", "shasum": "" }, "require": { - "php": ">=5.3.0" + "container-interop/container-interop": "^1.1", + "nikic/fast-route": "^1.0", + "php": ">=5.5.0", + "pimple/pimple": "^3.0", + "psr/http-message": "^1.0" }, - "suggest": { - "ext-mcrypt": "Required for HTTP cookie encryption" + "provide": { + "psr/http-message-implementation": "1.0" }, - "type": "library", - "autoload": { - "psr-0": { - "Slim": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Josh Lockhart", - "email": "info@joshlockhart.com", - "homepage": "http://www.joshlockhart.com/" - } - ], - "description": "Slim Framework, a PHP micro framework", - "homepage": "http://github.com/codeguy/Slim", - "keywords": [ - "microframework", - "rest", - "router" - ], - "time": "2015-03-08 18:41:17" - }, - { - "name": "slim/views", - "version": "0.1.3", - "source": { - "type": "git", - "url": "https://github.com/slimphp/Slim-Views.git", - "reference": "8561c785e55a39df6cb6f95c3aba3281a60ed5b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Views/zipball/8561c785e55a39df6cb6f95c3aba3281a60ed5b0", - "reference": "8561c785e55a39df6cb6f95c3aba3281a60ed5b0", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "slim/slim": ">=2.4.0" - }, - "suggest": { - "smarty/smarty": "Smarty templating system", - "twig/twig": "Twig templating system" + "require-dev": { + "phpunit/phpunit": "^4.0", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "autoload": { "psr-4": { - "Slim\\Views\\": "./" + "Slim\\": "Slim" } }, "notification-url": "https://packagist.org/downloads/", @@ -321,25 +516,36 @@ "MIT" ], "authors": [ + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, { "name": "Josh Lockhart", - "email": "info@joshlockhart.com", - "homepage": "http://www.joshlockhart.com/" + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" }, { "name": "Andrew Smith", "email": "a.smith@silentworks.co.uk", - "homepage": "http://thoughts.silentworks.co.uk/" + "homepage": "http://silentworks.co.uk" } ], - "description": "Smarty and Twig View Parser package for the Slim Framework", - "homepage": "http://github.com/codeguy/Slim-Views", + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "http://slimframework.com", "keywords": [ - "extensions", - "slimphp", - "templating" + "api", + "framework", + "micro", + "router" ], - "time": "2014-12-09 23:48:51" + "time": "2016-07-26 15:12:13" }, { "name": "smarty/smarty", @@ -397,17 +603,17 @@ "time": "2015-12-21 01:57:06" }, { - "name": "symfony/yaml", - "version": "v3.0.3", + "name": "symfony/process", + "version": "v3.1.2", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "b5ba64cd67ecd6887f63868fa781ca094bd1377c" + "url": "https://github.com/symfony/process.git", + "reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/b5ba64cd67ecd6887f63868fa781ca094bd1377c", - "reference": "b5ba64cd67ecd6887f63868fa781ca094bd1377c", + "url": "https://api.github.com/repos/symfony/process/zipball/5c11a1a4d4016662eeaf0f8757958c7de069f9a0", + "reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0", "shasum": "" }, "require": { @@ -416,7 +622,56 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-06-29 05:42:25" + }, + { + "name": "symfony/yaml", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de", + "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" } }, "autoload": { @@ -443,22 +698,22 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-02-23 15:16:06" + "time": "2016-06-29 05:41:56" } ], "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": { @@ -470,7 +725,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -504,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.3", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9a6a883c48acb215d4825ce9de61dccf93d62074" + "reference": "39492b8b8fe514163e677bf154fd80f6cc995759" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a6a883c48acb215d4825ce9de61dccf93d62074", - "reference": "9a6a883c48acb215d4825ce9de61dccf93d62074", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/39492b8b8fe514163e677bf154fd80f6cc995759", + "reference": "39492b8b8fe514163e677bf154fd80f6cc995759", "shasum": "" }, "require": { @@ -533,7 +788,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -567,12 +822,14 @@ "debug", "dump" ], - "time": "2016-02-13 09:23:44" + "time": "2016-06-29 05:41:56" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "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 0734d4d..9f66ab7 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,6 +1,12 @@ 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[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 8a1074f..9eed80f 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -11,8 +11,15 @@ * @link http://rudloff.pro * */ namespace Alltube\Controller; + use Alltube\VideoDownload; use Alltube\Config; +use Symfony\Component\Process\ProcessBuilder; +use Chain\Chain; +use Slim\Http\Stream; +use Slim\Http\Request; +use Slim\Http\Response; + /** * Main controller * @@ -26,177 +33,251 @@ use Alltube\Config; * */ class FrontController { + public function __construct($container) + { + $this->config = Config::getInstance(); + $this->download = new VideoDownload(); + $this->container = $container; + } /** * Display index page * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * * @return void */ - static function index() + public function index(Request $request, Response $response) { - global $app; - $config = Config::getInstance(); - $app->render( + $this->container->view->render( + $response, 'head.tpl', array( - 'class'=>'index' + 'class'=>'index', + 'description'=>'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.' ) ); - $app->render( + $this->container->view->render( + $response, 'header.tpl' ); - $app->render( + $this->container->view->render( + $response, 'index.tpl', array( - 'convert'=>$config->convert + 'convert'=>$this->config->convert ) ); - $app->render('footer.tpl'); + $this->container->view->render($response, 'footer.tpl'); } /** * Display a list of extractors * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * * @return void */ - static function extractors() + public function extractors(Request $request, Response $response) { - global $app; - $app->render( + $this->container->view->render( + $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' ) ); - $app->render('header.tpl'); - $app->render('logo.tpl'); - $app->render( + $this->container->view->render($response, 'header.tpl'); + $this->container->view->render($response, 'logo.tpl'); + $this->container->view->render( + $response, 'extractors.tpl', array( - 'extractors'=>VideoDownload::listExtractors() + 'extractors'=>$this->download->listExtractors() ) ); - $app->render('footer.tpl'); + $this->container->view->render($response, 'footer.tpl'); } /** * Dislay information about the video * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * * @return void */ - static function video() + public function video(Request $request, Response $response) { - global $app; - $config = Config::getInstance(); - if (isset($_GET["url"])) { - if (isset($_GET['audio'])) { + $params = $request->getQueryParams(); + $this->config = Config::getInstance(); + if (isset($params["url"])) { + if (isset($params['audio'])) { try { - $video = VideoDownload::getJSON($_GET["url"]); + $url = $this->download->getURL($params["url"], 'mp3[protocol^=http]'); + return $response->withRedirect($url); + } catch (\Exception $e) { + $video = $this->download->getJSON($params["url"], 'bestaudio/best'); + + if (!shell_exec('which '.$this->config->avconv)) { + throw(new \Exception('Can\'t find avconv or ffmpeg')); + } + + $avconvProc = ProcessBuilder::create( + array( + $this->config->avconv, + '-v', 'quiet', + '-i', '-', + '-f', 'mp3', + '-vn', + 'pipe:1' + ) + ); //Vimeo needs a correct user-agent - $UA = VideoDownload::getUA(); ini_set( 'user_agent', - $UA + $video->http_headers->{'User-Agent'} ); - $url_info = parse_url($video->url); - if ($url_info['scheme'] == 'rtmp') { - ob_end_flush(); - header( - 'Content-Disposition: attachment; filename="'. - html_entity_decode( - pathinfo( - VideoDownload::getFilename( - $video->webpage_url - ), PATHINFO_FILENAME - ).'.mp3', ENT_COMPAT, 'ISO-8859-1' - ).'"' + + $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') { + if (!shell_exec('which '.$this->config->rtmpdump)) { + throw(new \Exception('Can\'t find rtmpdump')); + } + $builder = new ProcessBuilder( + array( + $this->config->rtmpdump, + '-q', + '-r', + $video->url, + '--pageUrl', $video->webpage_url + ) ); - header("Content-Type: audio/mpeg"); - passthru( - '/usr/bin/rtmpdump -q -r '.escapeshellarg($video->url). - ' | '.$config->avconv. - ' -v quiet -i - -f mp3 -vn pipe:1' - ); - exit; + 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 { - ob_end_flush(); - header( - 'Content-Disposition: attachment; filename="'. - html_entity_decode( - pathinfo( - VideoDownload::getFilename( - $video->webpage_url - ), PATHINFO_FILENAME - ).'.mp3', ENT_COMPAT, 'ISO-8859-1' - ).'"' + if (!shell_exec('which curl')) { + throw(new \Exception('Can\'t find curl')); + } + $chain = new Chain( + ProcessBuilder::create( + array_merge( + array( + 'curl', + '--silent', + '--user-agent', $video->http_headers->{'User-Agent'}, + $video->url + ), + $this->config->curl_params + ) + ) ); - header("Content-Type: audio/mpeg"); - passthru( - 'curl '.$config->curl_params. - ' --user-agent '.escapeshellarg($UA). - ' '.escapeshellarg($video->url). - ' | '.$config->avconv. - ' -v quiet -i - -f mp3 -vn pipe:1' - ); - exit; + $chain->add('|', $avconvProc); } - } catch (\Exception $e) { - $error = $e->getMessage(); + if ($request->isGet()) { + $response = $response->withBody(new Stream(popen($chain->getProcess()->getCommandLine(), 'r'))); + } + return $response; } } else { - try { - $video = VideoDownload::getJSON($_GET["url"]); - $app->render( - 'head.tpl', - array( - 'class'=>'video' - ) - ); - $app->render( - 'video.tpl', - array( - 'video'=>$video - ) - ); - $app->render('footer.tpl'); - } catch (\Exception $e) { - $error = $e->getMessage(); - } + $video = $this->download->getJSON($params["url"]); + $this->container->view->render( + $response, + 'head.tpl', + array( + 'class'=>'video', + 'title'=>$video->title, + 'description'=>'Download "'.$video->title.'" from '.$video->extractor_key + ) + ); + $this->container->view->render( + $response, + 'video.tpl', + array( + 'video'=>$video + ) + ); + $this->container->view->render($response, 'footer.tpl'); } + } else { + return $response->withRedirect($this->container->get('router')->pathFor('index')); } - if (isset($error)) { - $app->render( - 'head.tpl', - array( - 'class'=>'video' - ) - ); - $app->render( - 'error.tpl', - array( - 'errors'=>$error - ) - ); - $app->render('footer.tpl'); - } + } + + public function error(Request $request, Response $response, \Exception $exception) + { + $this->container->view->render( + $response, + 'head.tpl', + array( + 'class'=>'video', + 'title'=>'Error' + ) + ); + $this->container->view->render( + $response, + 'error.tpl', + array( + 'errors'=>$exception->getMessage() + ) + ); + $this->container->view->render($response, 'footer.tpl'); + return $response->withStatus(500); } /** * Redirect to video file * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * * @return void */ - static function redirect() + public function redirect(Request $request, Response $response) { - global $app; - if (isset($_GET["url"])) { + $params = $request->getQueryParams(); + if (isset($params["url"])) { try { - $video = VideoDownload::getURL($_GET["url"]); - $app->redirect($video['url']); + $url = $this->download->getURL($params["url"], $params["format"]); + return $response->withRedirect($url); } catch (\Exception $e) { - $app->response->headers->set('Content-Type', 'text/plain'); - echo $e->getMessage().PHP_EOL; + $response->getBody()->write($e->getMessage()); + return $response->withHeader('Content-Type', 'text/plain'); } } } @@ -204,18 +285,22 @@ class FrontController /** * Output JSON info about the video * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * * @return void */ - static function json() + public function json(Request $request, Response $response) { - global $app; - if (isset($_GET["url"])) { - $app->response->headers->set('Content-Type', 'application/json'); + $params = $request->getQueryParams(); + if (isset($params["url"])) { try { - $video = VideoDownload::getJSON($_GET["url"]); - echo json_encode($video); + $video = $this->download->getJSON($params["url"]); + return $response->withJson($video); } catch (\Exception $e) { - echo json_encode(array('success'=>false, 'error'=>$e->getMessage())); + return $response->withJson( + array('success'=>false, 'error'=>$e->getMessage()) + ); } } } diff --git a/css/style.css b/css/style.css index c456cf5..1c5c8e7 100644 --- a/css/style.css +++ b/css/style.css @@ -8,8 +8,8 @@ body { -/************************HEADER******************************/ - +/************************HEADER******************************/ + header { position:absolute; top:0; @@ -17,11 +17,11 @@ header { width:100%; padding:0; } - + .social {padding-right:21px;} - + header a { overflow:hidden; @@ -37,7 +37,7 @@ background-repeat:no-repeat; -webkit-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in; -} +} header a:focus, header a:hover @@ -51,8 +51,8 @@ header a:hover .share {background-image:url('../img/share.png');} - -.sharemask + +.sharemask { height:38px; width:38px; @@ -64,11 +64,11 @@ background-image:url('../img/sharemask.png'); background-position:top left; background-repeat:no-repeat; } - + .facebook {background-image:url('../img/facebook.png');} - -.facebookmask + +.facebookmask { height:38px; width:38px; @@ -79,12 +79,12 @@ z-index:10; background-image:url('../img/facebookmask.png'); background-position:top left; background-repeat:no-repeat; -} - +} + .twitter {background-image:url('../img/twitter.png');} - -.twittermask + +.twittermask { height:38px; width:38px; @@ -95,13 +95,13 @@ z-index:10; background-image:url('../img/twittermask.png'); background-position:top left; background-repeat:no-repeat; -} +} + - /*************************FOOTER****************************/ - - + + footer { position:fixed; bottom:0; @@ -119,7 +119,7 @@ footer { .footer_wrapper { height:28px; } - + footer a{ color:#adadad; -webkit-transition: all 0.1s ease-in; @@ -137,7 +137,7 @@ color:#f2084a; -moz-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in; } - + @@ -180,7 +180,7 @@ margin-top:8px; text-decoration:none; display:inline-block; } - + .downloadBtn:focus, .downloadBtn:hover { @@ -248,7 +248,7 @@ color:#f2084a; -moz-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in; } - + #bookmarklet{ padding:15px; } @@ -265,7 +265,7 @@ padding-top:10px; padding-bottom:10px; border: 2px dotted; } - + .mp3 { position:relative; @@ -279,17 +279,17 @@ border: 2px dotted; text-align:left; font-weight:300; } - + .mp3 p { padding:3px; -} +} /* Demo CSS code */ - + .audio:not(:checked), .audio:checked { position: absolute; @@ -337,10 +337,10 @@ padding:3px; .audio:focus + label { color:black; } - + /* on checked */ .audio:checked + label:before { - background:#f2084a; + background:#f2084a; } .audio:checked + label:after { background: #fff; @@ -402,7 +402,7 @@ padding:3px; - + /*************************CONTENT COMPATIBLES****************************/ @@ -565,24 +565,29 @@ h1 { margin-bottom: 1em; } +.monospace { + font-family:monospace; +} + @media (max-width: 640px) { + .formats, .thumb { width:90%; } - + .URLinput{ min-width:0; } - + .logo { max-width:330px; } - + .logocompatible, .logocompatible img { max-width:447px; } - + .logocompatible, .logo, .champs, @@ -592,64 +597,70 @@ h1 { margin:auto; height:auto; } - + .logo { margin-top:50px; } - + .logocompatible img { width:100%; height: auto; } - + .downloadBtn { margin-top: 0.3em; } .mp3 { margin-bottom: 1em; } - + footer { display:none; } - + .tripleliste ul, .tripleliste { width:auto; margin-left:auto; margin-top:auto; } - + .logocompatiblemask { background:none; } - + .logocompatible { height:auto; background-image:none; background-color:#4F4F4F; } - + .logocompatiblemask, .logobis { width:auto; } - + .logocompatiblemask { position:static; } - + .logobis { height:auto; } - + .titre { margin:auto; } - + .error p { padding:0.5em; text-align:left; } - + +} + +@media all and (display-mode: standalone) { + .bookmarklet_wrapper { + display: none; + } } 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/img/favicon.png b/img/favicon.png index 217cb72..b097f07 100644 Binary files a/img/favicon.png and b/img/favicon.png differ diff --git a/img/logo.png b/img/logo.png index c31d06c..a5f2054 100644 Binary files a/img/logo.png and b/img/logo.png differ diff --git a/img/logo_250.png b/img/logo_250.png index a83aed0..fd3a030 100644 Binary files a/img/logo_250.png and b/img/logo_250.png differ diff --git a/img/logo_60.png b/img/logo_60.png index 338185e..9a60f0a 100644 Binary files a/img/logo_60.png and b/img/logo_60.png differ diff --git a/img/logo_90.png b/img/logo_90.png index a6b9a86..7964b7c 100644 Binary files a/img/logo_90.png and b/img/logo_90.png differ diff --git a/img/logo_app.png b/img/logo_app.png index 7ca733e..6fc6f37 100644 Binary files a/img/logo_app.png and b/img/logo_app.png differ diff --git a/img/logocompatiblemask.png b/img/logocompatiblemask.png index 85078f6..40de155 100644 Binary files a/img/logocompatiblemask.png and b/img/logocompatiblemask.png differ diff --git a/img/mp3hover.png b/img/mp3hover.png index c79fa6c..402cdfc 100644 Binary files a/img/mp3hover.png and b/img/mp3hover.png differ diff --git a/img/screenshot.png b/img/screenshot.png index 261ca65..c75201a 100644 Binary files a/img/screenshot.png and b/img/screenshot.png differ diff --git a/img/twitter.png b/img/twitter.png index f9cae65..c997081 100644 Binary files a/img/twitter.png and b/img/twitter.png differ diff --git a/index.php b/index.php index 181ffa0..e9cee6f 100644 --- a/index.php +++ b/index.php @@ -14,35 +14,47 @@ * */ require_once __DIR__.'/vendor/autoload.php'; use Alltube\VideoDownload; +use Alltube\Controller\FrontController; + +if (strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) { + header('Location: '.str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI'])); + die; +} + +$app = new \Slim\App(); +$container = $app->getContainer(); +$container['view'] = function ($c) { + $view = new \Slim\Views\Smarty(__DIR__.'/templates/'); + + $view->addSlimPlugins($c['router'], $c['request']->getUri()); + $view->registerPlugin('modifier', 'noscheme', 'Smarty_Modifier_noscheme'); + + + return $view; +}; + +$controller = new FrontController($container); + +$container['errorHandler'] = array($controller, 'error'); -$app = new \Slim\Slim( - array( - 'view' => new \Slim\Views\Smarty() - ) -); -$view = $app->view(); -$view->parserExtensions = array( - __DIR__.'/vendor/slim/views/SmartyPlugins', - __DIR__.'/vendor/rudloff/smarty-plugin-noscheme/' -); $app->get( '/', - array('Alltube\Controller\FrontController', 'index') -); + array($controller, 'index') +)->setName('index'); $app->get( '/extractors', - array('Alltube\Controller\FrontController', 'extractors') -)->name('extractors'); + array($controller, 'extractors') +)->setName('extractors'); $app->get( '/video', - array('Alltube\Controller\FrontController', 'video') -)->name('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/js/cast.js b/js/cast.js index 57261fe..0cc0bd3 100644 --- a/js/cast.js +++ b/js/cast.js @@ -1,97 +1,106 @@ /*global chrome*/ -/*jslint devel: true, browser: true */ -var launchBtn, disabledBtn, stopBtn; -var session, currentMedia; - -function receiverListener(e) { +/*jslint browser: true, nomen: true */ +var castModule = (function () { 'use strict'; - console.log('receiverListener', e); -} + var launchBtn, disabledBtn, stopBtn, session; -function onMediaDiscovered(how, media) { - 'use strict'; - console.log('onMediaDiscovered', how); - currentMedia = media; - if (launchBtn) { - stopBtn.classList.remove('cast_hidden'); - launchBtn.classList.add('cast_hidden'); + function receiverListener(e) { + return (e === chrome.cast.ReceiverAvailability.AVAILABLE); } -} -function sessionListener(e) { - 'use strict'; - session = e; - session.addMediaListener(onMediaDiscovered.bind(this, 'addMediaListener')); - if (session.media.length !== 0) { - onMediaDiscovered('onRequestSessionSuccess', session.media[0]); + function onMediaDiscovered() { + if (launchBtn) { + stopBtn.classList.remove('cast_hidden'); + launchBtn.classList.add('cast_hidden'); + } } -} -function onStopCast() { - 'use strict'; - stopBtn.classList.add('cast_hidden'); - launchBtn.classList.remove('cast_hidden'); -} - -function stopCast() { - 'use strict'; - session.stop(onStopCast); -} - -function onMediaError() { - 'use strict'; - console.log('onMediaError'); - stopCast(); -} - -function onRequestSessionSuccess(e) { - 'use strict'; - session = e; - var videoLink = document.getElementById('video_link'), videoURL = videoLink.dataset.video, mediaInfo = new chrome.cast.media.MediaInfo(videoURL, 'video/' + videoLink.dataset.ext), request = new chrome.cast.media.LoadRequest(mediaInfo); - session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError); -} - -function onLaunchError(e) { - 'use strict'; - console.log('onLaunchError', e.description); -} - -function launchCast() { - 'use strict'; - chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError); -} - -function onInitSuccess() { - 'use strict'; - launchBtn = document.getElementById('cast_btn_launch'); - disabledBtn = document.getElementById('cast_disabled'); - stopBtn = document.getElementById('cast_btn_stop'); - if (launchBtn) { - disabledBtn.classList.add('cast_hidden'); + function onStopCast() { + stopBtn.classList.add('cast_hidden'); launchBtn.classList.remove('cast_hidden'); - launchBtn.addEventListener('click', launchCast, false); - stopBtn.addEventListener('click', stopCast, false); } -} -function onError() { - 'use strict'; - console.log('onError'); -} - -function initializeCastApi() { - 'use strict'; - var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID), apiConfig = new chrome.cast.ApiConfig(sessionRequest, sessionListener, receiverListener, chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED); - chrome.cast.initialize(apiConfig, onInitSuccess, onError); -} - -function loadCastApi(loaded, errorInfo) { - 'use strict'; - if (loaded) { - initializeCastApi(); - } else { - console.log(errorInfo); + function onStopCastError(e) { + onStopCast(); + throw e.description; } -} -window['__onGCastApiAvailable'] = loadCastApi; + function updateListener() { + if (session.status !== chrome.cast.SessionStatus.CONNECTED) { + onStopCast(); + } + } + + function sessionListener(e) { + session = e; + session.addMediaListener(onMediaDiscovered.bind(this, 'addMediaListener')); + session.addUpdateListener(updateListener.bind(this)); + if (session.media.length !== 0) { + onMediaDiscovered('onRequestSessionSuccess', session.media[0]); + } + } + + function stopCast() { + session.stop(onStopCast, onStopCastError); + } + + function onMediaError(e) { + stopCast(); + throw e.description; + } + + function onRequestSessionSuccess(e) { + session = e; + var videoLink = document.getElementById('video_link'), videoURL = videoLink.dataset.video, mediaInfo = new chrome.cast.media.MediaInfo(videoURL, 'video/' + videoLink.dataset.ext), request = new chrome.cast.media.LoadRequest(mediaInfo); + session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError); + } + + function onLaunchError(e) { + throw e.description; + } + + function launchCast() { + chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError); + } + + function onInitSuccess() { + launchBtn = document.getElementById('cast_btn_launch'); + disabledBtn = document.getElementById('cast_disabled'); + stopBtn = document.getElementById('cast_btn_stop'); + if (launchBtn) { + disabledBtn.classList.add('cast_hidden'); + launchBtn.classList.remove('cast_hidden'); + launchBtn.addEventListener('click', launchCast, false); + stopBtn.addEventListener('click', stopCast, false); + } + } + + function onError(e) { + throw e.code; + } + + function initializeCastApi() { + var sessionRequest = new chrome.cast.SessionRequest(chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID), apiConfig = new chrome.cast.ApiConfig(sessionRequest, sessionListener, receiverListener, chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED); + chrome.cast.initialize(apiConfig, onInitSuccess, onError); + } + + function loadCastApi(loaded, errorInfo) { + if (loaded) { + initializeCastApi(); + } else { + throw errorInfo; + } + } + + return { + init: function () { + var intro = document.getElementById('download_intro'); + if (intro) { + intro.insertAdjacentHTML('beforeend', ' Google Cast™ Casting to ChromeCast…'); + window.__onGCastApiAvailable = loadCastApi; + } + } + }; +}()); + +window.addEventListener('load', castModule.init, false); 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/manifest.json b/manifest.json index 57256cf..9f08114 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,8 @@ { "short_name": "AllTube", "name": "AllTube Download", - "display": "minimal-ui", + "description": "Easily download videos from Youtube, Dailymotion, Vimeo and other websites", + "display": "standalone", "icons": [{ "src": "img/favicon.png", "sizes": "32x32", @@ -24,6 +25,7 @@ "type": "image/png" }], "lang": "en", - "start_url": "./index.php", - "theme_color": "#4F4F4F" + "start_url": "./", + "theme_color": "#4F4F4F", + "orientation": "portrait" } diff --git a/package.json b/package.json index 5aafc35..2620eab 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,25 @@ { "name": "alltube", - "version": "0.4.5", + "version": "0.5.0", "license": "GPL-3.0", "dependencies": { - "grunt": "~0.4.5", - "grunt-cli": "~0.1.13", + "grunt": "~1.0.1", + "grunt-cli": "~1.2.0", "grunt-contrib-cssmin": "~1.0.0", "grunt-contrib-uglify": "~1.0.0", - "grunt-contrib-watch": "~0.6.1", + "grunt-contrib-watch": "~1.0.0", "grunt-phpcs": "~0.4.0", "grunt-phpunit": "~0.3.6", - "grunt-contrib-compress": "~1.1.1", - "bower": "~1.7.1" + "grunt-contrib-compress": "~1.3.0", + "bower": "~1.7.1", + "grunt-githash": "~0.1.3", + "grunt-jslint": "~1.1.14" }, "repository": { "type": "git", "url": "https://github.com/Rudloff/alltube.git" }, "scripts": { - "postinstall": "./node_modules/bower/bin/bower install; ./node_modules/grunt-cli/bin/grunt" + "postinstall": "node node_modules/bower/bin/bower install && node node_modules/grunt-cli/bin/grunt" } } diff --git a/templates/error.tpl b/templates/error.tpl index 6756cf2..d000ab3 100644 --- a/templates/error.tpl +++ b/templates/error.tpl @@ -10,4 +10,3 @@ {/foreach}

-
diff --git a/templates/footer.tpl b/templates/footer.tpl index ae9c9be..cdbb327 100644 --- a/templates/footer.tpl +++ b/templates/footer.tpl @@ -1,10 +1,10 @@ + diff --git a/templates/head.tpl b/templates/head.tpl index 175293c..bf20a4c 100644 --- a/templates/head.tpl +++ b/templates/head.tpl @@ -1,31 +1,24 @@ - + - - - - - -AllTube Download - - - - - - - +{if isset($description)} + + + +{/if} + +AllTube Download{if isset($title)} - {$title|escape}{/if} + + + + - - + + - - - - - diff --git a/templates/header.tpl b/templates/header.tpl index dfa1df2..fc42083 100644 --- a/templates/header.tpl +++ b/templates/header.tpl @@ -1,8 +1,8 @@
- - +
diff --git a/templates/index.tpl b/templates/index.tpl index b6bdf87..5d046e6 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -1,7 +1,7 @@
-
-
+ @@ -20,10 +20,10 @@ {/if}
- See all supported websites -
+ See all supported websites +

Drag this to your bookmarks bar:

- Bookmarklet + Bookmarklet
diff --git a/templates/logo.tpl b/templates/logo.tpl index b063181..c008425 100644 --- a/templates/logo.tpl +++ b/templates/logo.tpl @@ -1,4 +1,4 @@

- -AllTube Download + +AllTube Download

diff --git a/templates/video.tpl b/templates/video.tpl index 3548013..daaf3cc 100644 --- a/templates/video.tpl +++ b/templates/video.tpl @@ -2,49 +2,85 @@
{include file="logo.tpl"} -

You are going to download +

You are going to download . -Google Cast™ is disabled -Google Cast™ -Casting to ChromeCast…

+

{if isset($video->thumbnail)} - + +{/if} +{if isset($video->description)} + +{/if} +{if isset($video->upload_date)} + {/if}
- {if isset($video->formats)} -

Available formats:

-

(You might have to do a Right click > Save as)

-

+

+
+ +

+
+
{else} - + Download
{/if} diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 718eb0f..c4fde67 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -10,6 +10,8 @@ * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ +namespace Alltube\Test; + use Alltube\Config; /** @@ -23,7 +25,7 @@ use Alltube\Config; * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ -class ConfigTest extends PHPUnit_Framework_TestCase +class ConfigTest extends \PHPUnit_Framework_TestCase { /** diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index fc84cf7..ce9e9b2 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -10,6 +10,8 @@ * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ +namespace Alltube\Test; + use Alltube\VideoDownload; /** @@ -23,26 +25,21 @@ use Alltube\VideoDownload; * @license GNU General Public License http://www.gnu.org/licenses/gpl.html * @link http://rudloff.pro * */ -class VideoDownloadTest extends PHPUnit_Framework_TestCase +class VideoDownloadTest extends \PHPUnit_Framework_TestCase { - /** - * Test getUA function - * - * @return void - */ - public function testGetUA() + protected function setUp() { - $this->assertStringStartsWith('Mozilla/', VideoDownload::getUA()); + $this->download = new VideoDownload(); } /** - * Test listExtractors funtion + * Test listExtractors function * * @return void */ public function testListExtractors() { - $extractors = VideoDownload::listExtractors(); + $extractors = $this->download->listExtractors(); $this->assertContains('youtube', $extractors); } @@ -55,11 +52,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); } /** @@ -73,7 +69,7 @@ class VideoDownloadTest extends PHPUnit_Framework_TestCase */ public function testGetURLError($url) { - $videoURL = VideoDownload::getURL($url); + $this->download->getURL($url); } /** @@ -86,16 +82,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' ), ); } @@ -112,22 +111,6 @@ class VideoDownloadTest extends PHPUnit_Framework_TestCase ); } - /** - * Test getFilename function - * - * @param string $url URL - * @param string $format Format - * @param string $result Expected filename - * - * @return void - * @dataProvider URLProvider - */ - public function testGetFilename($url, $format, $result) - { - $filename = VideoDownload::getFilename($url, $format); - $this->assertEquals($filename, $result); - } - /** * Test getJSON function * @@ -139,7 +122,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); @@ -159,6 +142,6 @@ class VideoDownloadTest extends PHPUnit_Framework_TestCase */ public function testGetJSONError($url) { - $videoURL = VideoDownload::getJSON($url); + $videoURL = $this->download->getJSON($url); } }