Merge branch 'release/0.10.0'

This commit is contained in:
Pierre Rudloff 2017-06-01 22:51:23 +02:00
commit 169a33dd50
53 changed files with 3905 additions and 595 deletions

3
.gitignore vendored
View file

@ -9,6 +9,7 @@ ffmpeg-*/
alltube-*.zip alltube-*.zip
coverage/ coverage/
bower_components/ bower_components/
config.yml config/config.yml
docs/ docs/
clover.xml clover.xml
i18n/*/LC_MESSAGES/*.mo

View file

@ -1,4 +1,4 @@
<ifmodule mod_expires.c> <ifmodule mod_mime.c>
AddType application/x-web-app-manifest+json .webapp AddType application/x-web-app-manifest+json .webapp
Addtype font/truetype .ttf Addtype font/truetype .ttf
</ifmodule> </ifmodule>
@ -15,13 +15,15 @@
FileETag None FileETag None
RewriteEngine On <ifmodule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC] RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC]
RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L] RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L] RewriteRule ^ index.php [QSA,L]
</ifmodule>
<ifmodule mod_filter.c> <ifmodule mod_filter.c>
AddOutputFilterByType DEFLATE text/css text/html application/javascript font/truetype AddOutputFilterByType DEFLATE text/css text/html application/javascript font/truetype

View file

@ -1,6 +1,9 @@
# Contributing
## Report a bug ## Report a bug
Before opening a new issue, make sure that: Before opening a new issue, make sure that:
* It has not already been [reported](https://github.com/Rudloff/alltube/issues). * It has not already been [reported](https://github.com/Rudloff/alltube/issues).
* You read the [README](README.md) and the [FAQ](FAQ.md). * You read the [README](README.md) and the [FAQ](resources/FAQ.md).
* You can provide **logs**. * You can provide **logs**.

View file

@ -7,7 +7,7 @@ RUN docker-php-ext-install intl
RUN docker-php-ext-install zip RUN docker-php-ext-install zip
RUN a2enmod rewrite RUN a2enmod rewrite
RUN curl -sS https://getcomposer.org/installer | php RUN curl -sS https://getcomposer.org/installer | php
COPY php.ini /usr/local/etc/php/ COPY resources/php.ini /usr/local/etc/php/
COPY . /var/www/html/ COPY . /var/www/html/
RUN php composer.phar install --prefer-dist RUN php composer.phar install --prefer-dist
RUN npm install RUN npm install

View file

@ -68,7 +68,7 @@ module.exports = function (grunt) {
options: { options: {
archive: 'alltube-<%= githash.main.tag %>.zip' archive: 'alltube-<%= githash.main.tag %>.zip'
}, },
src: ['*.php', '!config.yml', 'dist/**', '.htaccess', 'img/**', 'LICENSE', 'README.md', 'robots.txt', 'sitemap.xml', 'templates/**', 'templates_c/', 'vendor/**', 'classes/**', 'controllers/**', 'bower_components/**', '!vendor/ffmpeg/**', '!vendor/bin/ffmpeg', '!vendor/phpunit/**', '!vendor/squizlabs/**'] src: ['*.php', '!config/config.yml', 'dist/**', '.htaccess', 'img/**', 'LICENSE', 'README.md', 'robots.txt', 'resources/sitemap.xml', 'templates/**', 'templates_c/', 'vendor/**', 'classes/**', 'controllers/**', 'bower_components/**', '!vendor/ffmpeg/**', '!vendor/bin/ffmpeg', '!vendor/phpunit/**', '!vendor/squizlabs/**']
} }
}, },
phpdocumentor: { phpdocumentor: {
@ -80,7 +80,7 @@ module.exports = function (grunt) {
}, },
jsonlint: { jsonlint: {
manifests: { manifests: {
src: ['*.json', '*.webapp'], src: ['*.json', 'resources/*.json'],
options: { options: {
format: true format: true
} }
@ -90,6 +90,26 @@ module.exports = function (grunt) {
package: { package: {
src: 'package.json' src: 'package.json'
} }
},
potomo: {
dist: {
options: {
poDel: false
},
files: {
'i18n/fr_FR/LC_MESSAGES/Alltube.mo': 'i18n/fr_FR/LC_MESSAGES/Alltube.po',
'i18n/zh_CN/LC_MESSAGES/Alltube.mo': 'i18n/zh_CN/LC_MESSAGES/Alltube.po'
}
}
},
csslint: {
options: {
'box-sizing': false,
'bulletproof-font-face': false
},
css: {
src: 'css/*'
}
} }
} }
); );
@ -105,9 +125,11 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-phpdocumentor'); grunt.loadNpmTasks('grunt-phpdocumentor');
grunt.loadNpmTasks('grunt-jsonlint'); grunt.loadNpmTasks('grunt-jsonlint');
grunt.loadNpmTasks('grunt-fixpack'); grunt.loadNpmTasks('grunt-fixpack');
grunt.loadNpmTasks('grunt-potomo');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.registerTask('default', ['uglify', 'cssmin']); grunt.registerTask('default', ['uglify', 'cssmin', 'potomo']);
grunt.registerTask('lint', ['jslint', 'fixpack', 'jsonlint', 'phpcs']); grunt.registerTask('lint', ['jslint', 'csslint', 'fixpack', 'jsonlint', 'phpcs']);
grunt.registerTask('test', ['phpunit']); grunt.registerTask('test', ['phpunit']);
grunt.registerTask('doc', ['phpdocumentor']); grunt.registerTask('doc', ['phpdocumentor']);
grunt.registerTask('release', ['default', 'githash', 'compress']); grunt.registerTask('release', ['default', 'githash', 'compress']);

View file

@ -43,12 +43,16 @@ npm install
composer install composer install
``` ```
### On Heroku
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
## Config ## Config
If you want to use a custom config, you need to create a config file: If you want to use a custom config, you need to create a config file:
```bash ```bash
cp config.example.yml config.yml cp config/config.example.yml config/config.yml
``` ```
## PHP requirements ## PHP requirements
@ -168,7 +172,7 @@ You can also have a look at this [example project](https://github.com/Rudloff/al
## FAQ ## FAQ
Please read the [FAQ](FAQ.md) before reporting any issue. Please read the [FAQ](resources/FAQ.md) before reporting any issue.
## License ## License

38
app.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "AllTube Download",
"description": "HTML GUI for youtube-dl",
"repository": "https://github.com/Rudloff/alltube.git",
"logo": "https://alltubedownload.net/img/logo.png",
"keywords": [
"alltube",
"download",
"video",
"youtube",
"php"
],
"buildpacks": [
{
"url": "https://github.com/piotras/heroku-buildpack-gettext.git"
},
{
"url": "heroku/nodejs"
},
{
"url": "heroku/python"
},
{
"url": "heroku/php"
}
],
"env": {
"CONVERT": {
"description": "Enable audio conversion",
"value": "true"
},
"PYTHON": {
"description": "Path to python binary",
"value": "/app/.heroku/python/bin/python"
}
},
"website": "https://alltubedownload.net/"
}

View file

@ -38,7 +38,7 @@ class Config
* *
* @var array * @var array
*/ */
public $params = ['--no-warnings', '--ignore-errors', '--flat-playlist']; public $params = ['--no-warnings', '--ignore-errors', '--flat-playlist', '--restrict-filenames'];
/** /**
* Enable audio conversion. * Enable audio conversion.
@ -126,13 +126,13 @@ class Config
* *
* @return Config * @return Config
*/ */
public static function getInstance($yamlfile = 'config.yml') public static function getInstance($yamlfile = 'config/config.yml')
{ {
$yamlPath = __DIR__.'/../'.$yamlfile; $yamlPath = __DIR__.'/../'.$yamlfile;
if (is_null(self::$instance) || self::$instance->file != $yamlfile) { if (is_null(self::$instance) || self::$instance->file != $yamlfile) {
if (is_file($yamlfile)) { if (is_file($yamlfile)) {
$options = Yaml::parse(file_get_contents($yamlPath)); $options = Yaml::parse(file_get_contents($yamlPath));
} elseif ($yamlfile == 'config.yml' || empty($yamlfile)) { } elseif ($yamlfile == 'config/config.yml' || empty($yamlfile)) {
/* /*
Allow for the default file to be missing in order to Allow for the default file to be missing in order to
not surprise users that did not create a config file not surprise users that did not create a config file

95
classes/Locale.php Normal file
View file

@ -0,0 +1,95 @@
<?php
/**
* Locale class.
*/
namespace Alltube;
use Teto\HTTP\AcceptLanguage;
/**
* Class used to represent locales.
*/
class Locale
{
/**
* Locale language.
*
* @var string
*/
private $language;
/**
* Locale region.
*
* @var string
*/
private $region;
/**
* Locale constructor.
*
* @param string $locale ISO 15897 code
*/
public function __construct($locale)
{
$parse = AcceptLanguage::parse($locale);
$this->language = $parse[1]['language'];
$this->region = $parse[1]['region'];
}
/**
* Convert the locale to a string.
*
* @return string ISO 15897 code
*/
public function __toString()
{
return $this->getIso15897();
}
/**
* Get the full name of the locale.
*
* @return string
*/
public function getFullName()
{
return \Locale::getDisplayName($this->getIso15897(), $this->getIso15897());
}
/**
* Get the ISO 15897 code.
*
* @return string
*/
public function getIso15897()
{
return $this->language.'_'.$this->region;
}
/**
* Get the BCP 47 code.
*
* @return string
*/
public function getBcp47()
{
return $this->language.'-'.$this->region;
}
/**
* Get the ISO 3166 code.
*
* @return string
*/
public function getIso3166()
{
return strtolower($this->region);
}
public function getCountry()
{
return country($this->getIso3166());
}
}

87
classes/LocaleManager.php Normal file
View file

@ -0,0 +1,87 @@
<?php
/**
* LocaleManager class.
*/
namespace Alltube;
/**
* Class used to manage locales.
*/
class LocaleManager
{
/**
* Supported locales.
*
* @var array
*/
private $supportedLocales = ['en_US', 'fr_FR', 'zh_CN'];
/**
* Current locale.
*
* @var Locale
*/
private $curLocale;
/**
* Session segment used to store session variables.
*
* @var \Aura\Session\Segment
*/
private $sessionSegment;
/**
* LocaleManager constructor.
*
* @param array $cookies Cookie array
*/
public function __construct(array $cookies = [])
{
$session_factory = new \Aura\Session\SessionFactory();
$session = $session_factory->newInstance($cookies);
$this->sessionSegment = $session->getSegment('Alltube\LocaleManager');
$cookieLocale = $this->sessionSegment->get('locale');
if (isset($cookieLocale)) {
$this->setLocale(new Locale($cookieLocale));
}
}
/**
* Get a list of supported locales.
*
* @return Locale[]
*/
public function getSupportedLocales()
{
$return = [];
foreach ($this->supportedLocales as $supportedLocale) {
$return[] = new Locale($supportedLocale);
}
return $return;
}
/**
* Get the current locale.
*
* @return Locale
*/
public function getLocale()
{
return $this->curLocale;
}
/**
* Set the current locale.
*
* @param Locale $locale Locale
*/
public function setLocale(Locale $locale)
{
putenv('LANG='.$locale);
setlocale(LC_ALL, [$locale, $locale.'.utf8']);
$this->curLocale = $locale;
$this->sessionSegment->set('locale', $locale);
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* LocaleMiddleware class.
*/
namespace Alltube;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Response;
use Teto\HTTP\AcceptLanguage;
/**
* Detect user locale.
*/
class LocaleMiddleware
{
/**
* LocaleManager instance.
*
* @var LocaleManager
*/
private $localeManager;
/**
* LocaleMiddleware constructor.
*
* @param ContainerInterface $container Slim dependency container
*/
public function __construct(ContainerInterface $container)
{
$this->localeManager = $container->get('locale');
}
/**
* Test if a locale can be used for the current user.
*
* @param array $proposedLocale Locale array created by AcceptLanguage::parse()
*
* @return string Locale name if chosen, nothing otherwise
*/
public function testLocale(array $proposedLocale)
{
foreach ($this->localeManager->getSupportedLocales() as $locale) {
$parsedLocale = AcceptLanguage::parse($locale);
if (isset($proposedLocale['language'])
&& $parsedLocale[1]['language'] == $proposedLocale['language']
&& $parsedLocale[1]['region'] == $proposedLocale['region']
) {
return new Locale($proposedLocale['language'].'_'.$proposedLocale['region']);
}
}
}
/**
* Main middleware function.
*
* @param Request $request PSR request
* @param Response $response PSR response
* @param callable $next Next middleware
*
* @return Response
*/
public function __invoke(Request $request, Response $response, callable $next)
{
$headers = $request->getHeader('Accept-Language');
$curLocale = $this->localeManager->getLocale();
if (!isset($curLocale)) {
if (isset($headers[0])) {
$this->localeManager->setLocale(
AcceptLanguage::detect([$this, 'testLocale'], new Locale('en_US'), $headers[0])
);
} else {
$this->localeManager->setLocale(new Locale('en_US'));
}
}
return $next($request, $response);
}
}

View file

@ -0,0 +1,201 @@
<?php
/**
* PlaylistArchiveStream class.
*
* @codingStandardsIgnoreFile
*/
namespace Alltube;
use Barracuda\ArchiveStream\TarArchive;
/**
* Class used to create a Tar archive from playlists and stream it to the browser.
*
* @link http://php.net/manual/en/class.streamwrapper.php
*/
class PlaylistArchiveStream extends TarArchive
{
/**
* Files to add in the archive.
*
* @var array[]
*/
private $files;
/**
* Stream used to store data before it is sent to the browser.
*
* @var resource
*/
private $buffer;
/**
* Guzzle client.
*
* @var \GuzzleHttp\Client
*/
private $client;
/**
* VideoDownload instance.
*
* @var VideoDownload
*/
private $download;
/**
* Current file position in $files array.
*
* @var int
*/
private $curFile = 0;
/**
* Video format to download.
*
* @var string
*/
private $format;
/**
* PlaylistArchiveStream constructor.
*/
public function __construct()
{
$this->client = new \GuzzleHttp\Client();
$this->download = new VideoDownload();
}
/**
* Add data to the archive.
*
* @param string $data Data
*
* @return void
*/
protected function send($data)
{
$pos = ftell($this->buffer);
fwrite($this->buffer, $data);
fseek($this->buffer, $pos);
}
/**
* Called when fopen() is used on the stream.
*
* @param string $path Playlist path (should be playlist://url1;url2;.../format)
*
* @return bool
*/
public function stream_open($path)
{
$this->format = ltrim(parse_url($path, PHP_URL_PATH), '/');
$this->buffer = fopen('php://temp', 'r+');
foreach (explode(';', parse_url($path, PHP_URL_HOST)) as $url) {
$this->files[] = [
'url' => urldecode($url),
'headersSent'=> false,
'complete' => false,
'stream' => null,
];
}
return true;
}
/**
* Called when fwrite() is used on the stream.
*
* @return int
*/
public function stream_write()
{
//We don't support writing to a stream
return 0;
}
/**
* Called when fstat() is used on the stream.
*
* @return array
*/
public function stream_stat()
{
//We need this so Slim won't try to get the size of the stream
return [
'mode'=> 0010000,
];
}
/**
* Called when ftell() is used on the stream.
*
* @return int
*/
public function stream_tell()
{
return ftell($this->buffer);
}
/**
* Called when fseek() is used on the stream.
*
* @param int $offset Offset
*
* @return bool
*/
public function stream_seek($offset)
{
return fseek($this->buffer, $offset) == 0;
}
/**
* Called when feof() is used on the stream.
*
* @return bool
*/
public function stream_eof()
{
foreach ($this->files as $file) {
if (!$file['complete']) {
return false;
}
}
return true;
}
/**
* Called when fread() is used on the stream.
*
* @param int $count Number of bytes to read
*
* @return string
*/
public function stream_read($count)
{
if (!$this->files[$this->curFile]['headersSent']) {
$urls = $this->download->getUrl($this->files[$this->curFile]['url'], $this->format);
$response = $this->client->request('GET', $urls[0], ['stream' => true]);
$contentLengthHeaders = $response->getHeader('Content-Length');
$this->init_file_stream_transfer(
$this->download->getFilename($this->files[$this->curFile]['url'], $this->format),
$contentLengthHeaders[0]
);
$this->files[$this->curFile]['headersSent'] = true;
$this->files[$this->curFile]['stream'] = $response->getBody();
} elseif (!$this->files[$this->curFile]['stream']->eof()) {
$this->stream_file_part($this->files[$this->curFile]['stream']->read($count));
} elseif (!$this->files[$this->curFile]['complete']) {
$this->complete_file_stream();
$this->files[$this->curFile]['complete'] = true;
} elseif (isset($this->files[$this->curFile])) {
$this->curFile += 1;
}
return fread($this->buffer, $count);
}
}

View file

@ -218,9 +218,9 @@ class VideoDownload
* *
* @return \Symfony\Component\Process\Process Process * @return \Symfony\Component\Process\Process Process
*/ */
private function getRtmpProcess($video) private function getRtmpProcess(\stdClass $video)
{ {
if (!shell_exec('which '.$this->config->rtmpdump)) { if (!$this->checkCommand([$this->config->rtmpdump, '--help'])) {
throw(new \Exception('Can\'t find rtmpdump')); throw(new \Exception('Can\'t find rtmpdump'));
} }
$builder = new ProcessBuilder( $builder = new ProcessBuilder(
@ -240,6 +240,22 @@ class VideoDownload
return $builder->getProcess(); return $builder->getProcess();
} }
/**
* Check if a command runs successfully.
*
* @param array $command Command and arguments
*
* @return bool False if the command returns an error, true otherwise
*/
private function checkCommand(array $command)
{
$builder = ProcessBuilder::create($command);
$process = $builder->getProcess();
$process->run();
return $process->isSuccessful();
}
/** /**
* Get a process that runs avconv in order to convert a video to MP3. * Get a process that runs avconv in order to convert a video to MP3.
* *
@ -249,11 +265,11 @@ class VideoDownload
*/ */
private function getAvconvMp3Process($url) private function getAvconvMp3Process($url)
{ {
if (!shell_exec('which '.$this->config->avconv)) { if (!$this->checkCommand([$this->config->avconv, '-version'])) {
throw(new \Exception('Can\'t find avconv or ffmpeg')); throw(new \Exception('Can\'t find avconv or ffmpeg'));
} }
return ProcessBuilder::create( $builder = ProcessBuilder::create(
[ [
$this->config->avconv, $this->config->avconv,
'-v', 'quiet', '-v', 'quiet',
@ -265,6 +281,8 @@ class VideoDownload
'pipe:1', 'pipe:1',
] ]
); );
return $builder->getProcess();
} }
/** /**
@ -292,7 +310,7 @@ class VideoDownload
} else { } else {
$avconvProc = $this->getAvconvMp3Process($video->url); $avconvProc = $this->getAvconvMp3Process($video->url);
return popen($avconvProc->getProcess()->getCommandLine(), 'r'); return popen($avconvProc->getCommandLine(), 'r');
} }
} }
@ -305,7 +323,7 @@ class VideoDownload
*/ */
public function getM3uStream(\stdClass $video) public function getM3uStream(\stdClass $video)
{ {
if (!shell_exec('which '.$this->config->avconv)) { if (!$this->checkCommand([$this->config->avconv, '-version'])) {
throw(new \Exception('Can\'t find avconv or ffmpeg')); throw(new \Exception('Can\'t find avconv or ffmpeg'));
} }
@ -362,4 +380,23 @@ class VideoDownload
{ {
return popen($this->getRtmpProcess($video)->getCommandLine(), 'r'); return popen($this->getRtmpProcess($video)->getCommandLine(), 'r');
} }
/**
* Get a Tar stream containing every video in the playlist piped through the server.
*
* @param object $video Video object returned by youtube-dl
* @param string $format Requested format
*
* @return Response HTTP response
*/
public function getPlaylistArchiveStream(\stdClass $video, $format)
{
$playlistItems = [];
foreach ($video->entries as $entry) {
$playlistItems[] = urlencode($entry->url);
}
$stream = fopen('playlist://'.implode(';', $playlistItems).'/'.$format, 'r');
return $stream;
}
} }

View file

@ -12,22 +12,25 @@
"symfony/process": "~3.2.0", "symfony/process": "~3.2.0",
"ptachoire/process-builder-chain": "~1.2.0", "ptachoire/process-builder-chain": "~1.2.0",
"guzzlehttp/guzzle": "~6.2.0", "guzzlehttp/guzzle": "~6.2.0",
"rudloff/rtmpdump-bin": "~2.3", "aura/session": "~2.1.0",
"aura/session": "~2.1.0" "barracudanetworks/archivestream-php": "~1.0.5",
"smarty-gettext/smarty-gettext": "~1.5.1",
"zonuexe/http-accept-language": "~0.4.1",
"rinvex/country": "~2.0.0"
}, },
"require-dev": { "require-dev": {
"symfony/var-dumper": "~3.2.0", "symfony/var-dumper": "~3.2.0",
"squizlabs/php_codesniffer": "~2.8.0", "squizlabs/php_codesniffer": "~3.0.0",
"phpunit/phpunit": "~5.7.2", "phpunit/phpunit": "~5.7.2",
"ffmpeg/ffmpeg": "dev-release", "ffmpeg/ffmpeg": "dev-release",
"rg3/youtube-dl": "~2017.04.28", "rg3/youtube-dl": "~2017.05.09",
"rudloff/rtmpdump-bin": "~2.3", "rudloff/rtmpdump-bin": "~2.3.0",
"heroku/heroku-buildpack-php": "*" "heroku/heroku-buildpack-php": "*"
}, },
"extra": { "extra": {
"paas": { "paas": {
"nginx-includes": [ "nginx-includes": [
"nginx.conf" "resources/nginx.conf"
] ]
} }
}, },
@ -36,10 +39,10 @@
"type": "package", "type": "package",
"package": { "package": {
"name": "rg3/youtube-dl", "name": "rg3/youtube-dl",
"version": "2017.04.28", "version": "2017.05.09",
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.28.zip" "url": "https://github.com/rg3/youtube-dl/archive/2017.05.09.zip"
} }
} }
}, },

419
composer.lock generated
View file

@ -4,8 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "0a4bd158955df2e3f5ea89714ba9e740", "content-hash": "22e16312bcf339c90850660d9e5923e7",
"content-hash": "cb0e773496b6f26caf1c8591f23488f5",
"packages": [ "packages": [
{ {
"name": "aura/session", "name": "aura/session",
@ -67,7 +66,47 @@
"session", "session",
"sessions" "sessions"
], ],
"time": "2016-10-03 20:28:32" "time": "2016-10-03T20:28:32+00:00"
},
{
"name": "barracudanetworks/archivestream-php",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/barracudanetworks/ArchiveStream-php.git",
"reference": "1bf98097d1e9b137fd40081f26abb0a17b097ef7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barracudanetworks/ArchiveStream-php/zipball/1bf98097d1e9b137fd40081f26abb0a17b097ef7",
"reference": "1bf98097d1e9b137fd40081f26abb0a17b097ef7",
"shasum": ""
},
"require": {
"ext-gmp": "*",
"ext-mbstring": "*",
"php": ">=5.1.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Barracuda\\ArchiveStream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A library for dynamically streaming dynamic tar or zip files without the need to have the complete file stored on the server.",
"homepage": "https://github.com/barracudanetworks/ArchiveStream-php",
"keywords": [
"archive",
"php",
"stream",
"tar",
"zip"
],
"time": "2017-01-13T14:52:38+00:00"
}, },
{ {
"name": "container-interop/container-interop", "name": "container-interop/container-interop",
@ -98,7 +137,7 @@
], ],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)", "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop", "homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14 19:40:03" "time": "2017-02-14T19:40:03+00:00"
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
@ -160,7 +199,7 @@
"rest", "rest",
"web service" "web service"
], ],
"time": "2017-02-28 22:50:30" "time": "2017-02-28T22:50:30+00:00"
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
@ -211,7 +250,7 @@
"keywords": [ "keywords": [
"promise" "promise"
], ],
"time": "2016-12-20 10:07:11" "time": "2016-12-20T10:07:11+00:00"
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
@ -276,7 +315,7 @@
"uri", "uri",
"url" "url"
], ],
"time": "2017-03-20 17:10:46" "time": "2017-03-20T17:10:46+00:00"
}, },
{ {
"name": "mathmarques/smarty-view", "name": "mathmarques/smarty-view",
@ -326,7 +365,7 @@
"template", "template",
"view" "view"
], ],
"time": "2016-08-25 19:04:49" "time": "2016-08-25T19:04:49+00:00"
}, },
{ {
"name": "nikic/fast-route", "name": "nikic/fast-route",
@ -369,7 +408,7 @@
"router", "router",
"routing" "routing"
], ],
"time": "2017-01-19 11:35:12" "time": "2017-01-19T11:35:12+00:00"
}, },
{ {
"name": "pimple/pimple", "name": "pimple/pimple",
@ -415,7 +454,7 @@
"container", "container",
"dependency injection" "dependency injection"
], ],
"time": "2015-09-11 15:10:35" "time": "2015-09-11T15:10:35+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -464,7 +503,7 @@
"container-interop", "container-interop",
"psr" "psr"
], ],
"time": "2017-02-14 16:28:37" "time": "2017-02-14T16:28:37+00:00"
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
@ -514,7 +553,7 @@
"request", "request",
"response" "response"
], ],
"time": "2016-08-06 14:39:51" "time": "2016-08-06T14:39:51+00:00"
}, },
{ {
"name": "ptachoire/process-builder-chain", "name": "ptachoire/process-builder-chain",
@ -550,35 +589,81 @@
} }
], ],
"description": "Add ability to chain symfony processes", "description": "Add ability to chain symfony processes",
"time": "2016-04-10 08:33:20" "time": "2016-04-10T08:33:20+00:00"
}, },
{ {
"name": "rudloff/rtmpdump-bin", "name": "rinvex/country",
"version": "2.3", "version": "v2.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Rudloff/rtmpdump-bin.git", "url": "https://github.com/rinvex/country.git",
"reference": "133cdd80e3bab66593e88a5276158596383afd97" "reference": "9405da035644bc76671bfba0c282fef8fd3408e1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Rudloff/rtmpdump-bin/zipball/133cdd80e3bab66593e88a5276158596383afd97", "url": "https://api.github.com/repos/rinvex/country/zipball/9405da035644bc76671bfba0c282fef8fd3408e1",
"reference": "133cdd80e3bab66593e88a5276158596383afd97", "reference": "9405da035644bc76671bfba0c282fef8fd3408e1",
"shasum": "" "shasum": ""
}, },
"require-dev": { "require": {
"rtmpdump/rtmpdump": "2.3" "php": ">=5.5.9"
},
"require-dev": {
"phpunit/phpunit": "^5.2.0"
}, },
"bin": [
"rtmpdump"
],
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Rinvex\\Country\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"GPL-2.0" "MIT"
], ],
"description": "rtmpdump binary for Linux 64 bit", "authors": [
"time": "2016-04-12 19:17:32" {
"name": "Rinvex LLC",
"email": "help@rinvex.com",
"homepage": "https://rinvex.com"
},
{
"name": "Abdelrahman Omran",
"email": "me@omranic.com",
"homepage": "https://omranic.com",
"role": "Project Lead"
},
{
"name": "The Generous Laravel Community",
"homepage": "https://github.com/rinvex/country/contributors"
}
],
"description": "Rinvex Country is a simple and lightweight package for retrieving country details with flexibility. A whole bunch of data including name, demonym, capital, iso codes, dialling codes, geo data, currencies, flags, emoji, and other attributes for all 250 countries worldwide at your fingertips.",
"homepage": "https://rinvex.com",
"keywords": [
"Flexible",
"Simple",
"countries",
"country",
"currencies",
"demonym",
"dialling",
"emoji",
"flags",
"geographic",
"languages",
"rinvex",
"svg"
],
"time": "2016-11-27T05:53:11+00:00"
}, },
{ {
"name": "slim/slim", "name": "slim/slim",
@ -649,7 +734,61 @@
"micro", "micro",
"router" "router"
], ],
"time": "2017-03-19 17:55:20" "time": "2017-03-19T17:55:20+00:00"
},
{
"name": "smarty-gettext/smarty-gettext",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/smarty-gettext/smarty-gettext.git",
"reference": "00fe2fcbc41e24e0245cd9d73f96bc7b0337972d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smarty-gettext/smarty-gettext/zipball/00fe2fcbc41e24e0245cd9d73f96bc7b0337972d",
"reference": "00fe2fcbc41e24e0245cd9d73f96bc7b0337972d",
"shasum": ""
},
"require": {
"ext-gettext": "*",
"ext-pcre": "*",
"php": "~5.3|~7.0"
},
"require-dev": {
"azatoth/php-pgettext": "~1.0",
"smarty/smarty": "3.1.*"
},
"suggest": {
"azatoth/php-pgettext": "Support msgctxt for {t} via context parameter"
},
"bin": [
"tsmarty2c.php"
],
"type": "library",
"autoload": {
"files": [
"block.t.php",
"function.locale.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1+"
],
"authors": [
{
"name": "Sagi Bashari",
"email": "sagi@boom.org.il"
},
{
"name": "Elan Ruusamäe",
"email": "glen@delfi.ee"
}
],
"description": "Gettext plugin enabling internationalization in Smarty Package files",
"homepage": "https://github.com/smarty-gettext/smarty-gettext",
"time": "2017-05-12T12:14:46+00:00"
}, },
{ {
"name": "smarty/smarty", "name": "smarty/smarty",
@ -702,20 +841,20 @@
"keywords": [ "keywords": [
"templating" "templating"
], ],
"time": "2016-12-14 21:57:25" "time": "2016-12-14T21:57:25+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v3.2.8", "version": "v3.2.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0" "reference": "36774717bbd1631be2d0a45acf48aecd5836c867"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", "url": "https://api.github.com/repos/symfony/process/zipball/36774717bbd1631be2d0a45acf48aecd5836c867",
"reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", "reference": "36774717bbd1631be2d0a45acf48aecd5836c867",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -751,20 +890,20 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-04-12 14:13:17" "time": "2017-05-08T01:51:21+00:00"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v3.2.8", "version": "v3.2.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/yaml.git",
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6" "reference": "4cdb9fec28fba88203a71f6d095018867f7a2065"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6", "url": "https://api.github.com/repos/symfony/yaml/zipball/4cdb9fec28fba88203a71f6d095018867f7a2065",
"reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6", "reference": "4cdb9fec28fba88203a71f6d095018867f7a2065",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -806,7 +945,48 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2017-05-01 14:55:58" "time": "2017-05-25T23:42:36+00:00"
},
{
"name": "zonuexe/http-accept-language",
"version": "0.4.1",
"source": {
"type": "git",
"url": "https://github.com/BaguettePHP/http-accept-language.git",
"reference": "f71422b1200737aa9d7c7fa83f07cbe4616198d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/BaguettePHP/http-accept-language/zipball/f71422b1200737aa9d7c7fa83f07cbe4616198d5",
"reference": "f71422b1200737aa9d7c7fa83f07cbe4616198d5",
"shasum": ""
},
"require": {
"ext-intl": "*"
},
"require-dev": {
"phploc/phploc": "*",
"phpunit/phpunit": "4.1.*",
"theseer/phpdox": "0.6.*"
},
"type": "library",
"autoload": {
"psr-4": {
"Teto\\HTTP\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "USAMI Kenta",
"email": "tadsan@zonu.me"
}
],
"description": "HTTP Accept-Language Header parser",
"time": "2014-10-19T09:22:18+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -862,7 +1042,7 @@
"constructor", "constructor",
"instantiate" "instantiate"
], ],
"time": "2015-06-14 21:17:01" "time": "2015-06-14T21:17:01+00:00"
}, },
{ {
"name": "ffmpeg/ffmpeg", "name": "ffmpeg/ffmpeg",
@ -920,7 +1100,7 @@
"nginx", "nginx",
"php" "php"
], ],
"time": "2017-03-27 23:33:27" "time": "2017-03-27T23:33:27+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -962,7 +1142,7 @@
"object", "object",
"object graph" "object graph"
], ],
"time": "2017-04-12 18:52:22" "time": "2017-04-12T18:52:22+00:00"
}, },
{ {
"name": "phpdocumentor/reflection-common", "name": "phpdocumentor/reflection-common",
@ -1016,7 +1196,7 @@
"reflection", "reflection",
"static analysis" "static analysis"
], ],
"time": "2015-12-27 11:43:31" "time": "2015-12-27T11:43:31+00:00"
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
@ -1061,7 +1241,7 @@
} }
], ],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2016-09-30 07:12:33" "time": "2016-09-30T07:12:33+00:00"
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
@ -1108,7 +1288,7 @@
"email": "me@mikevanriel.com" "email": "me@mikevanriel.com"
} }
], ],
"time": "2016-11-25 06:54:22" "time": "2016-11-25T06:54:22+00:00"
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
@ -1171,7 +1351,7 @@
"spy", "spy",
"stub" "stub"
], ],
"time": "2017-03-02 20:05:34" "time": "2017-03-02T20:05:34+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -1234,7 +1414,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2017-04-02 07:44:40" "time": "2017-04-02T07:44:40+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -1281,7 +1461,7 @@
"filesystem", "filesystem",
"iterator" "iterator"
], ],
"time": "2016-10-03 07:40:28" "time": "2016-10-03T07:40:28+00:00"
}, },
{ {
"name": "phpunit/php-text-template", "name": "phpunit/php-text-template",
@ -1322,7 +1502,7 @@
"keywords": [ "keywords": [
"template" "template"
], ],
"time": "2015-06-21 13:50:34" "time": "2015-06-21T13:50:34+00:00"
}, },
{ {
"name": "phpunit/php-timer", "name": "phpunit/php-timer",
@ -1371,7 +1551,7 @@
"keywords": [ "keywords": [
"timer" "timer"
], ],
"time": "2017-02-26 11:10:40" "time": "2017-02-26T11:10:40+00:00"
}, },
{ {
"name": "phpunit/php-token-stream", "name": "phpunit/php-token-stream",
@ -1420,20 +1600,20 @@
"keywords": [ "keywords": [
"tokenizer" "tokenizer"
], ],
"time": "2017-02-27 10:12:30" "time": "2017-02-27T10:12:30+00:00"
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "5.7.19", "version": "5.7.20",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1" "reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3cb94a5f8c07a03c8b7527ed7468a2926203f58b",
"reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1", "reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1451,7 +1631,7 @@
"phpunit/php-timer": "^1.0.6", "phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.2", "phpunit/phpunit-mock-objects": "^3.2",
"sebastian/comparator": "^1.2.4", "sebastian/comparator": "^1.2.4",
"sebastian/diff": "~1.2", "sebastian/diff": "^1.4.3",
"sebastian/environment": "^1.3.4 || ^2.0", "sebastian/environment": "^1.3.4 || ^2.0",
"sebastian/exporter": "~2.0", "sebastian/exporter": "~2.0",
"sebastian/global-state": "^1.1", "sebastian/global-state": "^1.1",
@ -1502,7 +1682,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2017-04-03 02:22:27" "time": "2017-05-22T07:42:55+00:00"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -1561,19 +1741,47 @@
"mock", "mock",
"xunit" "xunit"
], ],
"time": "2016-12-08 20:27:08" "time": "2016-12-08T20:27:08+00:00"
}, },
{ {
"name": "rg3/youtube-dl", "name": "rg3/youtube-dl",
"version": "2017.04.28", "version": "2017.05.09",
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://github.com/rg3/youtube-dl/archive/2017.04.28.zip", "url": "https://github.com/rg3/youtube-dl/archive/2017.05.09.zip",
"reference": null, "reference": null,
"shasum": null "shasum": null
}, },
"type": "library" "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-12T19:17:32+00:00"
},
{ {
"name": "sebastian/code-unit-reverse-lookup", "name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.1", "version": "1.0.1",
@ -1617,7 +1825,7 @@
], ],
"description": "Looks up which function or method a line of code belongs to", "description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"time": "2017-03-04 06:30:41" "time": "2017-03-04T06:30:41+00:00"
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
@ -1681,27 +1889,27 @@
"compare", "compare",
"equality" "equality"
], ],
"time": "2017-01-29 09:50:25" "time": "2017-01-29T09:50:25+00:00"
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
"version": "1.4.1", "version": "1.4.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/diff.git", "url": "https://github.com/sebastianbergmann/diff.git",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": "^5.3.3 || ^7.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.8" "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -1733,7 +1941,7 @@
"keywords": [ "keywords": [
"diff" "diff"
], ],
"time": "2015-12-08 07:14:41" "time": "2017-05-22T07:24:03+00:00"
}, },
{ {
"name": "sebastian/environment", "name": "sebastian/environment",
@ -1783,7 +1991,7 @@
"environment", "environment",
"hhvm" "hhvm"
], ],
"time": "2016-11-26 07:53:53" "time": "2016-11-26T07:53:53+00:00"
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
@ -1850,7 +2058,7 @@
"export", "export",
"exporter" "exporter"
], ],
"time": "2016-11-19 08:54:04" "time": "2016-11-19T08:54:04+00:00"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@ -1901,7 +2109,7 @@
"keywords": [ "keywords": [
"global state" "global state"
], ],
"time": "2015-10-12 03:26:01" "time": "2015-10-12T03:26:01+00:00"
}, },
{ {
"name": "sebastian/object-enumerator", "name": "sebastian/object-enumerator",
@ -1947,7 +2155,7 @@
], ],
"description": "Traverses array structures and object graphs to enumerate all referenced objects", "description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/", "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2017-02-18 15:18:39" "time": "2017-02-18T15:18:39+00:00"
}, },
{ {
"name": "sebastian/recursion-context", "name": "sebastian/recursion-context",
@ -2000,7 +2208,7 @@
], ],
"description": "Provides functionality to recursively process PHP variables", "description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context", "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2016-11-19 07:33:16" "time": "2016-11-19T07:33:16+00:00"
}, },
{ {
"name": "sebastian/resource-operations", "name": "sebastian/resource-operations",
@ -2042,7 +2250,7 @@
], ],
"description": "Provides a list of PHP built-in functions that operate on resources", "description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations", "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"time": "2015-07-28 20:34:47" "time": "2015-07-28T20:34:47+00:00"
}, },
{ {
"name": "sebastian/version", "name": "sebastian/version",
@ -2085,68 +2293,41 @@
], ],
"description": "Library that helps with managing the version number of Git-hosted PHP projects", "description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version", "homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03 07:35:21" "time": "2016-10-03T07:35:21+00:00"
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
"version": "2.8.1", "version": "3.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" "reference": "b95ff2c3b122a3ee4b57d149a57d2afce65522c3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b95ff2c3b122a3ee4b57d149a57d2afce65522c3",
"reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", "reference": "b95ff2c3b122a3ee4b57d149a57d2afce65522c3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"php": ">=5.1.2" "php": ">=5.4.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "~4.0"
}, },
"bin": [ "bin": [
"scripts/phpcs", "bin/phpcs",
"scripts/phpcbf" "bin/phpcbf"
], ],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.x-dev" "dev-master": "3.x-dev"
} }
}, },
"autoload": {
"classmap": [
"CodeSniffer.php",
"CodeSniffer/CLI.php",
"CodeSniffer/Exception.php",
"CodeSniffer/File.php",
"CodeSniffer/Fixer.php",
"CodeSniffer/Report.php",
"CodeSniffer/Reporting.php",
"CodeSniffer/Sniff.php",
"CodeSniffer/Tokens.php",
"CodeSniffer/Reports/",
"CodeSniffer/Tokenizers/",
"CodeSniffer/DocGenerators/",
"CodeSniffer/Standards/AbstractPatternSniff.php",
"CodeSniffer/Standards/AbstractScopeSniff.php",
"CodeSniffer/Standards/AbstractVariableSniff.php",
"CodeSniffer/Standards/IncorrectPatternException.php",
"CodeSniffer/Standards/Generic/Sniffs/",
"CodeSniffer/Standards/MySource/Sniffs/",
"CodeSniffer/Standards/PEAR/Sniffs/",
"CodeSniffer/Standards/PSR1/Sniffs/",
"CodeSniffer/Standards/PSR2/Sniffs/",
"CodeSniffer/Standards/Squiz/Sniffs/",
"CodeSniffer/Standards/Zend/Sniffs/"
]
},
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"BSD-3-Clause" "BSD-3-Clause"
@ -2163,7 +2344,7 @@
"phpcs", "phpcs",
"standards" "standards"
], ],
"time": "2017-03-01 22:17:45" "time": "2017-05-04T00:33:04+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
@ -2222,20 +2403,20 @@
"portable", "portable",
"shim" "shim"
], ],
"time": "2016-11-14 01:06:16" "time": "2016-11-14T01:06:16+00:00"
}, },
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
"version": "v3.2.8", "version": "v3.2.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/var-dumper.git", "url": "https://github.com/symfony/var-dumper.git",
"reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8" "reference": "eda0d0891a0b60a25015f7b85ea8040b51d72e1e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa47963ac7979ddbd42b2d646d1b056bddbf7bb8", "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eda0d0891a0b60a25015f7b85ea8040b51d72e1e",
"reference": "fa47963ac7979ddbd42b2d646d1b056bddbf7bb8", "reference": "eda0d0891a0b60a25015f7b85ea8040b51d72e1e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2290,7 +2471,7 @@
"debug", "debug",
"dump" "dump"
], ],
"time": "2017-05-01 14:55:58" "time": "2017-05-15T12:02:31+00:00"
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
@ -2340,7 +2521,7 @@
"check", "check",
"validate" "validate"
], ],
"time": "2016-11-23 20:04:58" "time": "2016-11-23T20:04:58+00:00"
} }
], ],
"aliases": [], "aliases": [],

View file

@ -6,6 +6,7 @@
namespace Alltube\Controller; namespace Alltube\Controller;
use Alltube\Config; use Alltube\Config;
use Alltube\Locale;
use Alltube\PasswordException; use Alltube\PasswordException;
use Alltube\VideoDownload; use Alltube\VideoDownload;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
@ -61,6 +62,13 @@ class FrontController
*/ */
private $defaultFormat = 'best[protocol^=http]'; private $defaultFormat = 'best[protocol^=http]';
/**
* LocaleManager instance.
*
* @var LocaleManager
*/
private $localeManager;
/** /**
* FrontController constructor. * FrontController constructor.
* *
@ -78,6 +86,7 @@ class FrontController
$this->download = new VideoDownload(); $this->download = new VideoDownload();
$this->container = $container; $this->container = $container;
$this->view = $this->container->get('view'); $this->view = $this->container->get('view');
$this->localeManager = $this->container->get('locale');
$session_factory = new \Aura\Session\SessionFactory(); $session_factory = new \Aura\Session\SessionFactory();
$session = $session_factory->newInstance($cookies); $session = $session_factory->newInstance($cookies);
$this->sessionSegment = $session->getSegment('Alltube\Controller\FrontController'); $this->sessionSegment = $session->getSegment('Alltube\Controller\FrontController');
@ -101,18 +110,35 @@ class FrontController
$response, $response,
'index.tpl', 'index.tpl',
[ [
'convert' => $this->config->convert, 'config' => $this->config,
'uglyUrls' => $this->config->uglyUrls, 'class' => 'index',
'class' => 'index', 'description' => 'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.',
'description' => 'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.', 'domain' => $uri->getScheme().'://'.$uri->getAuthority(),
'domain' => $uri->getScheme().'://'.$uri->getAuthority(), 'canonical' => $this->getCanonicalUrl($request),
'canonical' => $this->getCanonicalUrl($request), 'supportedLocales' => $this->localeManager->getSupportedLocales(),
'locale' => $this->localeManager->getLocale(),
] ]
); );
return $response; return $response;
} }
/**
* Switch locale.
*
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
* @param array $data Query parameters
*
* @return Response
*/
public function locale(Request $request, Response $response, array $data)
{
$this->localeManager->setLocale(new Locale($data['locale']));
return $response->withRedirect($this->container->get('router')->pathFor('index'));
}
/** /**
* Display a list of extractors. * Display a list of extractors.
* *
@ -133,6 +159,7 @@ class FrontController
'description' => 'List of all supported websites from which Alltube Download '. 'description' => 'List of all supported websites from which Alltube Download '.
'can extract video or audio files', 'can extract video or audio files',
'canonical' => $this->getCanonicalUrl($request), 'canonical' => $this->getCanonicalUrl($request),
'locale' => $this->localeManager->getLocale(),
] ]
); );
@ -157,6 +184,7 @@ class FrontController
'title' => 'Password prompt', 'title' => 'Password prompt',
'description' => 'You need a password in order to download this video with Alltube Download', 'description' => 'You need a password in order to download this video with Alltube Download',
'canonical' => $this->getCanonicalUrl($request), 'canonical' => $this->getCanonicalUrl($request),
'locale' => $this->localeManager->getLocale(),
] ]
); );
@ -247,8 +275,7 @@ class FrontController
'protocol' => $protocol, 'protocol' => $protocol,
'config' => $this->config, 'config' => $this->config,
'canonical' => $this->getCanonicalUrl($request), 'canonical' => $this->getCanonicalUrl($request),
'uglyUrls' => $this->config->uglyUrls, 'locale' => $this->localeManager->getLocale(),
'remux' => $this->config->remux,
] ]
); );
@ -300,6 +327,7 @@ class FrontController
'class' => 'video', 'class' => 'video',
'title' => 'Error', 'title' => 'Error',
'canonical' => $this->getCanonicalUrl($request), 'canonical' => $this->getCanonicalUrl($request),
'locale' => $this->localeManager->getLocale(),
] ]
); );
@ -320,26 +348,32 @@ class FrontController
private function getStream($url, $format, Response $response, Request $request, $password = null) private function getStream($url, $format, Response $response, Request $request, $password = null)
{ {
$video = $this->download->getJSON($url, $format, $password); $video = $this->download->getJSON($url, $format, $password);
if ($video->protocol == 'rtmp') { if (isset($video->entries)) {
$stream = $this->download->getPlaylistArchiveStream($video, $format);
$response = $response->withHeader('Content-Type', 'application/x-tar');
$response = $response->withHeader(
'Content-Disposition',
'attachment; filename="'.$video->title.'.tar"'
);
return $response->withBody(new Stream($stream));
} elseif ($video->protocol == 'rtmp') {
$stream = $this->download->getRtmpStream($video); $stream = $this->download->getRtmpStream($video);
$response = $response->withHeader('Content-Type', 'video/'.$video->ext); $response = $response->withHeader('Content-Type', 'video/'.$video->ext);
if ($request->isGet()) { $body = new Stream($stream);
$response = $response->withBody(new Stream($stream));
}
} elseif ($video->protocol == 'm3u8') { } elseif ($video->protocol == 'm3u8') {
$stream = $this->download->getM3uStream($video); $stream = $this->download->getM3uStream($video);
$response = $response->withHeader('Content-Type', 'video/'.$video->ext); $response = $response->withHeader('Content-Type', 'video/'.$video->ext);
if ($request->isGet()) { $body = new Stream($stream);
$response = $response->withBody(new Stream($stream));
}
} else { } else {
$client = new \GuzzleHttp\Client(); $client = new \GuzzleHttp\Client();
$stream = $client->request('GET', $video->url, ['stream' => true]); $stream = $client->request('GET', $video->url, ['stream' => true]);
$response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
$response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
if ($request->isGet()) { $body = $stream->getBody();
$response = $response->withBody($stream->getBody()); }
} if ($request->isGet()) {
$response = $response->withBody($body);
} }
$response = $response->withHeader( $response = $response->withHeader(
'Content-Disposition', 'Content-Disposition',
@ -353,7 +387,7 @@ class FrontController
/** /**
* Get a remuxed stream piped through the server. * Get a remuxed stream piped through the server.
* *
* @param array $urls URLs of the video and audio files * @param string[] $urls URLs of the video and audio files
* @param string $format Requested format * @param string $format Requested format
* @param Response $response PSR-7 response * @param Response $response PSR-7 response
* @param Request $request PSR-7 request * @param Request $request PSR-7 request
@ -429,6 +463,10 @@ class FrontController
$this->sessionSegment->getFlash($url) $this->sessionSegment->getFlash($url)
); );
} else { } else {
if (empty($videoUrls[0])) {
throw new \Exception("Can't find URL of video");
}
return $response->withRedirect($videoUrls[0]); return $response->withRedirect($videoUrls[0]);
} }
} }

View file

@ -10,3 +10,11 @@
font-weight: 400; font-weight: 400;
src: local('Open Sans'), local('OpenSans'), url(../bower_components/opensans-googlefont/OpenSans-Regular.ttf); src: local('Open Sans'), local('OpenSans'), url(../bower_components/opensans-googlefont/OpenSans-Regular.ttf);
} }
.small-font {
font-size: 13px;
}
.large-font {
font-size:24px;
}

View file

@ -1,119 +1,118 @@
body { body {
text-align:center;
background-color: #EBEBEB; background-color: #EBEBEB;
background-image:url('../img/fond.jpg'); background-image:url('../img/fond.jpg');
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
font-weight:400; font-weight:400;
text-align:center;
} }
/************************HEADER******************************/ /* Header */
header { header {
position:absolute;
top:0;
text-align:right;
width:100%;
padding:0; padding:0;
} position:absolute;
text-align:right;
top:0;
width:100%;
}
.social .social {
{padding-right:21px;} padding-right:21px;
}
header a header .social a {
{ background-position:0 0;
overflow:hidden; background-repeat:no-repeat;
height:38px; float:right;
width:38px; height:38px;
position:relative; margin-left:13px;
float:right; margin-right:0;
margin-top:13px; margin-top:13px;
margin-left:13px; overflow:hidden;
margin-right:0; position:relative;
background-position:0 0; -webkit-transition: all 0.1s ease-in;
background-repeat:no-repeat; -moz-transition: all 0.1s ease-in;
-webkit-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in; width:38px;
-o-transition: all 0.1s ease-in;
} }
header a:focus, header a:focus,
header a:hover header a:hover {
{
outline:none;
background-position:0 100%; background-position:0 100%;
-webkit-transition: all 0.1s ease-in; outline:none;
-moz-transition: all 0.1s ease-in; -webkit-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;
} }
.share .share {
{background-image:url('../img/share.png');} background-image:url('../img/share.png');
.sharemask
{
height:38px;
width:38px;
position:absolute;
top:0;
left:0;
z-index:10;
background-image:url('../img/sharemask.png');
background-position:top left;
background-repeat:no-repeat;
} }
.facebook .sharemask {
{background-image:url('../img/facebook.png');} background-image:url('../img/sharemask.png');
background-position:top left;
background-repeat:no-repeat;
height:38px;
left:0;
position:absolute;
top:0;
width:38px;
z-index:10;
.facebookmask
{
height:38px;
width:38px;
position:absolute;
top:0;
left:0;
z-index:10;
background-image:url('../img/facebookmask.png');
background-position:top left;
background-repeat:no-repeat;
} }
.twitter .facebook {
{background-image:url('../img/twitter.png');} background-image:url('../img/facebook.png');
}
.twittermask .facebookmask {
{ background-image:url('../img/facebookmask.png');
height:38px; background-position:top left;
width:38px; background-repeat:no-repeat;
position:absolute; height:38px;
top:0; left:0;
left:0; position:absolute;
z-index:10; top:0;
background-image:url('../img/twittermask.png'); width:38px;
background-position:top left; z-index:10;
background-repeat:no-repeat; }
.twitter {
background-image:url('../img/twitter.png');
}
.twittermask {
background-image:url('../img/twittermask.png');
background-position:top left;
background-repeat:no-repeat;
height:38px;
left:0;
position:absolute;
top:0;
width:38px;
z-index:10;
} }
/*************************FOOTER****************************/ /* Footer */
footer { footer {
position:fixed; background-image:url('../img/fondfooter.png');
background-position:top left;
background-repeat:repeat-x;
bottom:0; bottom:0;
color:#adadad;
padding-top:20px;
position:fixed;
text-align:center; text-align:center;
width:100%; width:100%;
background-image:url('../img/fondfooter.png');
background-repeat:repeat-x;
background-position:top left;
padding-top:20px;
color:#adadad;
font-size:12px;
z-index:11; z-index:11;
} }
@ -123,20 +122,19 @@ footer {
footer a{ footer a{
color:#adadad; color:#adadad;
-webkit-transition: all 0.1s ease-in; text-decoration:none;
-moz-transition: all 0.1s ease-in; -webkit-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
text-decoration:none; -o-transition: all 0.1s ease-in;
} }
footer a:focus, footer a:focus,
footer a:hover footer a:hover {
{ color:#f2084a;
outline:none; outline:none;
color:#f2084a; -webkit-transition: all 0.1s ease-in;
-webkit-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;
} }
@ -144,52 +142,47 @@ color:#f2084a;
/*************************CONTENT ACCUEIL****************************/ /* Home content */
.logo { .logo {
padding-bottom:55px; padding-bottom:55px;
} }
.labelurl .labelurl {
{ color:#3f3f3f;
position:relative; font-size:19px;
color:#3f3f3f; position:relative;
font-size:19px;
} }
.champs .champs {
{ margin-bottom:70px;
position:relative; margin-top:8px;
margin-bottom:70px; position:relative;
margin-top:8px;
} }
.downloadBtn { .downloadBtn {
position:relative;
background-color:#3A3A3A; background-color:#3A3A3A;
border: 3px solid #a5a5a5; border: 3px solid #a5a5a5;
color:#dedede;
border-radius:10px; border-radius:10px;
padding: 12px 14px; color:#dedede;
font-size:24px;
font-weight:800;
cursor:pointer; cursor:pointer;
display:inline-block;
font-weight:800;
padding: 12px 14px;
position:relative;
text-decoration:none;
-webkit-transition: all 0.1s ease-in; -webkit-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
text-decoration:none;
display:inline-block;
} }
.downloadBtn:focus, .downloadBtn:focus,
.downloadBtn:hover .downloadBtn:hover {
{
outline:none;
background-color:#f2084a; background-color:#f2084a;
-webkit-transition: all 0.1s ease-in; outline:none;
-moz-transition: all 0.1s ease-in; -webkit-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;
} }
.downloadBtn::-moz-focus-inner { .downloadBtn::-moz-focus-inner {
@ -197,22 +190,21 @@ margin-top:8px;
} }
.URLinput{ .URLinput{
position:relative;
background-color:#fff; background-color:#fff;
border: 3px solid #a5a5a5; border: 3px solid #a5a5a5;
color:#3F3F3F;
border-radius:10px; border-radius:10px;
padding: 12px 12px 12px 12px; color:#3F3F3F;
min-width:426px;
font-size:24px;
font-weight:800; font-weight:800;
margin-right:8px; margin-right:8px;
min-width:426px;
padding: 12px 12px 12px 12px;
position:relative;
} }
.URLinput:focus { .URLinput:focus {
outline: none;
border-color:#3A3A3A; border-color:#3A3A3A;
outline: none;
} }
.URLinput:-webkit-input-placeholder{ .URLinput:-webkit-input-placeholder{
@ -223,176 +215,163 @@ margin-top:8px;
} }
.combatiblelink { .combatiblelink {
position:relative; background-image:url('../img/compatiblerouage.png');
color:#a5a5a5; background-position:0 100%;
font-size:13px; background-repeat:no-repeat;
z-index:10; color:#a5a5a5;
text-decoration:none; padding-bottom:10px;
background-image:url('../img/compatiblerouage.png'); padding-left:41px;
background-position:0 100%; padding-top:10px;
background-repeat:no-repeat; position:relative;
padding-left:41px; text-decoration:none;
padding-top:10px; -webkit-transition: all 0.1s ease-in;
padding-bottom:10px; -moz-transition: all 0.1s ease-in;
-webkit-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in; z-index:10;
-o-transition: all 0.1s ease-in;
} }
.combatiblelink:focus, .combatiblelink:focus,
.combatiblelink:hover .combatiblelink:hover {
{ background-position:0 0;
color:#f2084a;
outline:none; outline:none;
background-position:0 0; -webkit-transition: all 0.1s ease-in;
color:#f2084a; -moz-transition: all 0.1s ease-in;
-webkit-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;
} }
#bookmarklet{ .bookmarklet {
padding:15px; border: 2px dotted;
} color:gray;
padding:10px 30px;
.bookmarklet{
position:relative;
font-size:13px;
color:gray;
z-index:10;
text-decoration:none;
padding-left:30px;
padding-right:30px;
padding-top:10px;
padding-bottom:10px;
border: 2px dotted;
}
.mp3
{
position:relative; position:relative;
text-decoration:none;
z-index:10;
}
.mp3 {
background-color:#cecece; background-color:#cecece;
color:#696969;
border-radius:6px; border-radius:6px;
width:622px; color:#696969;
font-size:14px;
height:26px; height:26px;
margin-top:12px; margin-top:12px;
position:relative;
text-align:left; text-align:left;
font-weight:300; width:622px;
} }
.mp3 p .mp3 p {
{ padding:3px;
padding:3px;
} }
/* .audio:not(:checked),
Demo CSS code .audio:checked {
*/ left: -9999px;
position: absolute;
}
.audio:not(:checked) + label,
.audio:checked + label {
cursor: pointer;
line-height:22px;
padding-left: 82px;
position: relative;
}
.audio:not(:checked) + label:before,
.audio:checked + label:before,
.audio:not(:checked) + label:after,
.audio:checked + label:after {
content: '';
position: absolute;
}
.audio:not(:checked) + label:before,
.audio:checked + label:before {
background: #ffffff;
border-radius: 6px;
height: 20px;
left:0;
top: -1px;
-webkit-transition: background-color .2s;
-moz-transition: background-color .2s;
-ms-transition: background-color .2s;
-o-transition: background-color .2s;
transition: background-color .2s;
width: 45px;
}
.audio:not(:checked) + label:after,
.audio:checked + label:after {
background: #3a3a3a;
border-radius: 6px;
height: 16px;
left: 2px;
top: 1px;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-ms-transition: all .2s;
-o-transition: all .2s;
transition: all .2s;
width: 16px;
}
.audio:not(:checked), .audio:focus + label {
.audio:checked { color:black;
position: absolute; }
left: -9999px;
}
.audio:not(:checked) + label,
.audio:checked + label {
position: relative;
padding-left: 82px;
cursor: pointer;
line-height:22px;
}
.audio:not(:checked) + label:before,
.audio:checked + label:before,
.audio:not(:checked) + label:after,
.audio:checked + label:after {
content: '';
position: absolute;
}
.audio:not(:checked) + label:before,
.audio:checked + label:before {
left:0; top: -1px;
width: 45px; height: 20px;
background: #ffffff;
border-radius: 6px;
-webkit-transition: background-color .2s;
-moz-transition: background-color .2s;
-ms-transition: background-color .2s;
-o-transition: background-color .2s;
transition: background-color .2s;
}
.audio:not(:checked) + label:after,
.audio:checked + label:after {
width: 16px; height: 16px;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-ms-transition: all .2s;
-o-transition: all .2s;
transition: all .2s;
border-radius: 6px;
background: #3a3a3a;
top: 1px; left: 2px;
}
.audio:focus + label { /* on checked */
color:black; .audio:checked + label:before {
} background:#f2084a;
}
.audio:checked + label:after {
background: #fff;
left: 27px;
top: 1px;
}
/* on checked */ .audio:checked + label .ui,
.audio:checked + label:before { .audio:not(:checked) + label .ui:before,
background:#f2084a; .audio:checked + label .ui:after {
} border-radius: 15px;
.audio:checked + label:after { font-size: 11px;
background: #fff; font-weight: bold;
top: 1px; left: 27px; height:20px;
} left: 3px;
line-height: 17px;
.audio:checked + label .ui, position: absolute;
.audio:not(:checked) + label .ui:before, -webkit-transition: all .2s;
.audio:checked + label .ui:after { -moz-transition: all .2s;
position: absolute; -ms-transition: all .2s;
left: 3px; -o-transition: all .2s;
width: 45px; transition: all .2s;
border-radius: 15px; width: 45px;
font-size: 11px; }
font-weight: bold; .audio:not(:checked) + label .ui:before {
line-height: 17px; background-image:url('../img/mp3hover.png');
height:20px; background-position:right top;
-webkit-transition: all .2s; background-repeat:no-repeat;
-moz-transition: all .2s; content: "no";
-ms-transition: all .2s; left: 0;
-o-transition: all .2s; min-width:56px;
transition: all .2s; padding-left:23px;
} padding-top:2px;
.audio:not(:checked) + label .ui:before { -webkit-transition: all .2s;
content: "no"; -moz-transition: all .2s;
left: 0; -ms-transition: all .2s;
padding-left:23px; -o-transition: all .2s;
padding-top:2px; transition: all .2s;
background-image:url('../img/mp3hover.png'); }
background-repeat:no-repeat; .audio:checked + label .ui:after {
background-position:right top; background-image:url('../img/mp3.png');
width:56px; background-position:right top;
-webkit-transition: all .2s; background-repeat:no-repeat;
-moz-transition: all .2s; color: #fff;
-ms-transition: all .2s; content: "yes";
-o-transition: all .2s; -webkit-transition: all .2s;
transition: all .2s; -moz-transition: all .2s;
} -ms-transition: all .2s;
.audio:checked + label .ui:after { -o-transition: all .2s;
content: "yes"; transition: all .2s;
color: #fff; width:73px;
background-image:url('../img/mp3.png'); }
background-repeat:no-repeat;
background-position:right top;
width:73px;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-ms-transition: all .2s;
-o-transition: all .2s;
transition: all .2s;
}
@ -410,120 +389,114 @@ padding:3px;
width: 600px; width: 600px;
} }
.playlist-entry h3 { .playlist-entry-title {
margin-top: 0; margin-top: 0;
} }
.playlist-entry h3 a { .playlist-entry-title a {
text-decoration: none; text-decoration: none;
} }
.playlist-entry h3 a:hover { .playlist-entry-title a:hover {
text-decoration: underline; text-decoration: underline;
} }
.playlist-entry .downloadBtn { .playlist-entry .downloadBtn {
font-size: 16px;
border-width: 2px; border-width: 2px;
font-size: 16px;
} }
/*************************CONTENT COMPATIBLES****************************/ /* Supported websites list */
.logobis .logobis {
{ height:107px;
width:447px; margin:0 auto 10px auto;
height:107px; position:relative;
position:relative; width:447px;
margin:0 auto 10px auto;
} }
.logocompatible .logocompatible {
{ background-image:url('../img/logocompatible.png');
width:447px; background-position:0 0;
height:107px; background-repeat:repeat-y;
background-image:url('../img/logocompatible.png'); display:block;
background-repeat:repeat-y; height:107px;
background-position:0 0; -webkit-transition: all 0.1s ease-in;
-webkit-transition: all 0.1s ease-in; -moz-transition: all 0.1s ease-in;
-moz-transition: all 0.1s ease-in; -o-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in; width:447px;
display:block;
} }
.logocompatible:focus, .logocompatible:focus,
.logocompatible:hover { .logocompatible:hover {
outline:none;
background-position:0 100%; background-position:0 100%;
-webkit-transition: all 0.1s ease-in; outline:none;
-moz-transition: all 0.1s ease-in; -webkit-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;} -moz-transition: all 0.1s ease-in;
-o-transition: all 0.1s ease-in;
.logocompatiblemask
{
z-index:10;
position:absolute;
top:0;
left:0;
width:447px;
height:107px;
background-image:url('../img/logocompatiblemask.png');
background-position:0 100%;
background-repeat:no-repeat;
}
.titre
{
font-family: 'Open Sans', sans-serif;
font-weight:300;
color:#383838;
font-size:48px;
}
.tripleliste
{
margin-top:80px;
width:800px;
position:relative;
margin-left:auto;
margin-right:auto;
} }
.tripleliste ul .logocompatiblemask {
{ background-image:url('../img/logocompatiblemask.png');
margin-bottom:1em; background-position:0 100%;
width:600px; background-repeat:no-repeat;
margin-left:120px;} height:107px;
left:0;
position:absolute;
top:0;
width:447px;
z-index:10;
}
.tripleliste ul li .titre {
{text-align:left; color:#383838;
List-Style-Type:none; font-family: 'Open Sans', sans-serif;
color:#383838; font-size:48px;
font-size:16px; font-weight:300;
}
width:200px; .tripleliste {
float:left; margin-left:auto;
position:relative; margin-right:auto;
margin-top:80px;
position:relative;
width:800px;
}
.tripleliste ul {
margin-bottom:1em;
margin-left:120px;
width:600px;
}
.tripleliste ul li {
color:#383838;
float:left;
list-style-type:none;
position:relative;
text-align:left;
width:200px;
} }
html, html,
body { body {
margin:0;
height:100%; height:100%;
margin:0;
} }
.wrapper { .wrapper {
height:100%;
display:table;
margin:auto;
padding-bottom:110px;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
display:table;
height:100%;
margin:auto;
padding-bottom:110px;
} }
.main { .main {
display:table-cell; display:table-cell;
@ -595,6 +568,65 @@ h1 {
font-family:monospace; font-family:monospace;
} }
.locales {
float: left;
padding-left: 1em;
padding-top: 1em;
text-align: left;
}
.locales a,
.locales a:visited {
color: #696969;
text-decoration: none;
}
.supportedLocales {
background-color: #fff;
list-style-type: none;
margin: 0;
opacity: 0;
padding-left: 0;
transition: visibility 0.5s;
visibility: hidden;
}
.supportedLocales li {
border-bottom: thin solid #E1E1E1;
}
.supportedLocales li:last-child {
border-bottom: none;
}
.supportedLocales li a {
display: block;
padding: 1em;
padding-right: 2em;
}
.supportedLocales li:hover {
background-color: #cecece;
}
.localesBtn {
background-color: transparent;
border: none;
cursor: pointer;
display: inline-block;
padding: 1em;
}
.localesBtn:focus {
background-color: #fff;
pointer-events: none;
}
.localesBtn:focus + .supportedLocales {
opacity: 1;
visibility: visible;
}
@media (max-width: 640px) { @media (max-width: 640px) {
.formats, .formats,
.thumb { .thumb {
@ -619,9 +651,9 @@ h1 {
.champs, .champs,
.URLinput, .URLinput,
.mp3 { .mp3 {
width:90%;
margin:auto;
height:auto; height:auto;
margin:auto;
width:90%;
} }
.logo { .logo {
@ -629,8 +661,8 @@ h1 {
} }
.logocompatible img { .logocompatible img {
width:100%;
height: auto; height: auto;
width:100%;
} }
.downloadBtn { .downloadBtn {
@ -646,9 +678,9 @@ h1 {
.tripleliste ul, .tripleliste ul,
.tripleliste { .tripleliste {
width:auto;
margin-left:auto; margin-left:auto;
margin-top:auto; margin-top:auto;
width:auto;
} }
.logocompatiblemask { .logocompatiblemask {
@ -656,9 +688,9 @@ h1 {
} }
.logocompatible { .logocompatible {
height:auto;
background-image:none;
background-color:#4F4F4F; background-color:#4F4F4F;
background-image:none;
height:auto;
} }
.logocompatiblemask, .logocompatiblemask,

View file

@ -0,0 +1,134 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: Pierre Rudloff <contact@rudloff.pro>\n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.10\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: templates/error.tpl:5
msgid "An error occured"
msgstr "Une erreur est survenue"
#: templates/error.tpl:6
msgid "Please check the URL of your video."
msgstr "Veuillez vérifier l'URL de votre vidéo"
#: templates/playlist.tpl:5
msgid "Videos extracted from"
msgstr "Vidéos extraites depuis"
#: templates/playlist.tpl:7
msgid ":"
msgstr " :"
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:85
#: templates/video.tpl:88 templates/index.tpl:18
msgid "Download"
msgstr "Télécharger"
#: templates/playlist.tpl:27
msgid "More options"
msgstr "Plus d'options"
#: templates/password.tpl:5
msgid "This video is protected"
msgstr "Cette vidéo est protégée"
#: templates/password.tpl:6
msgid "You need a password in order to download this video."
msgstr "L'accès à cette vidéo nécessite un mot de passe."
#: templates/password.tpl:8
msgid "Video password"
msgstr "Mot de passe de la vidéo"
#: templates/extractors.tpl:4
msgid "Supported websites"
msgstr "Sites web supportés"
#: templates/video.tpl:6
msgid "You are going to download"
msgstr "Vous allez télécharger"
#: templates/video.tpl:26
msgid "Available formats:"
msgstr "Formats disponibles :"
#: templates/video.tpl:31
msgid "Generic formats"
msgstr "Formats génériques"
#: templates/video.tpl:34
msgid "Best"
msgstr "Meilleure qualité"
#: templates/video.tpl:39
msgid "Remux best video with best audio"
msgstr "Combiner la meilleure vidéo avec le meilleur audio"
#: templates/video.tpl:43
msgid "Worst"
msgstr "Pire qualité"
#: templates/video.tpl:46
msgid "Detailed formats"
msgstr "Formats détaillés"
#: templates/inc/footer.tpl:4
msgid "Code by"
msgstr "Développé par"
#: templates/inc/footer.tpl:6
msgid "Design by"
msgstr "Designé par"
#: templates/inc/footer.tpl:12
msgid "AllTube Download on Facebook"
msgstr "AllTube Download sur Facebook"
#: templates/inc/footer.tpl:12
msgid "Like us on Facebook"
msgstr "Suivez-nous sur Facebook"
#: templates/inc/footer.tpl:14
msgid "Get the code"
msgstr "Obtenir le code"
#: templates/inc/footer.tpl:16
msgid "Based on"
msgstr "Basé sur"
#: templates/inc/header.tpl:4
msgid "Share on Twitter"
msgstr "Partager sur Twitter"
#: templates/inc/header.tpl:5
msgid "Share on Facebook"
msgstr "Partager sur Facebook"
#: templates/index.tpl:8
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
msgstr "Copiez ici l'URL de votre vidéo (Youtube, Dailymotion, etc.)"
#: templates/index.tpl:23
msgid "Audio only (MP3)"
msgstr "Audio uniquement (MP3)"
#: templates/index.tpl:28
msgid "See all supported websites"
msgstr "Voir tous les sites supportés"
#: templates/index.tpl:30
msgid "Drag this to your bookmarks bar:"
msgstr "Glissez ce lien dans votre barre de favoris :"
#: templates/index.tpl:31
msgid "Bookmarklet"
msgstr "Bookmarklet"

123
i18n/template.pot Normal file
View file

@ -0,0 +1,123 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
#: templates/error.tpl:5
msgid "An error occured"
msgstr ""
#: templates/error.tpl:6
msgid "Please check the URL of your video."
msgstr ""
#: templates/playlist.tpl:5
msgid "Videos extracted from"
msgstr ""
#: templates/playlist.tpl:7
msgid ":"
msgstr ""
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:85
#: templates/video.tpl:88 templates/index.tpl:18
msgid "Download"
msgstr ""
#: templates/playlist.tpl:27
msgid "More options"
msgstr ""
#: templates/password.tpl:5
msgid "This video is protected"
msgstr ""
#: templates/password.tpl:6
msgid "You need a password in order to download this video."
msgstr ""
#: templates/password.tpl:8
msgid "Video password"
msgstr ""
#: templates/extractors.tpl:4
msgid "Supported websites"
msgstr ""
#: templates/video.tpl:6
msgid "You are going to download"
msgstr ""
#: templates/video.tpl:26
msgid "Available formats:"
msgstr ""
#: templates/video.tpl:31
msgid "Generic formats"
msgstr ""
#: templates/video.tpl:34
msgid "Best"
msgstr ""
#: templates/video.tpl:39
msgid "Remux best video with best audio"
msgstr ""
#: templates/video.tpl:43
msgid "Worst"
msgstr ""
#: templates/video.tpl:46
msgid "Detailed formats"
msgstr ""
#: templates/inc/footer.tpl:4
msgid "Code by"
msgstr ""
#: templates/inc/footer.tpl:6
msgid "Design by"
msgstr ""
#: templates/inc/footer.tpl:12
msgid "AllTube Download on Facebook"
msgstr ""
#: templates/inc/footer.tpl:12
msgid "Like us on Facebook"
msgstr ""
#: templates/inc/footer.tpl:14
msgid "Get the code"
msgstr ""
#: templates/inc/footer.tpl:16
msgid "Based on"
msgstr ""
#: templates/inc/header.tpl:4
msgid "Share on Twitter"
msgstr ""
#: templates/inc/header.tpl:5
msgid "Share on Facebook"
msgstr ""
#: templates/index.tpl:8
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
msgstr ""
#: templates/index.tpl:23
msgid "Audio only (MP3)"
msgstr ""
#: templates/index.tpl:28
msgid "See all supported websites"
msgstr ""
#: templates/index.tpl:30
msgid "Drag this to your bookmarks bar:"
msgstr ""
#: templates/index.tpl:31
msgid "Bookmarklet"
msgstr ""

View file

@ -0,0 +1,135 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: Shiqiang Yu <tony19955569@gmail.com>\n"
"Language-Team: \n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.2\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: ..\..\..\templates/error.tpl:5
msgid "An error occured"
msgstr "出错了"
#: ..\..\..\templates/error.tpl:6
msgid "Please check the URL of your video."
msgstr "请检查您的视频的 URL。"
#: ..\..\..\templates/extractors.tpl:4
msgid "Supported websites"
msgstr "支持的网站"
#: ..\..\..\templates/inc/footer.tpl:4
msgid "Code by"
msgstr "代码来自"
#: ..\..\..\templates/inc/footer.tpl:6
msgid "Design by"
msgstr "设计来自"
#: ..\..\..\templates/inc/footer.tpl:12
msgid "AllTube Download on Facebook"
msgstr "去Alltube Download的Facebook页面"
#: ..\..\..\templates/inc/footer.tpl:12
msgid "Like us on Facebook"
msgstr "在Facebook关注我们"
#: ..\..\..\templates/inc/footer.tpl:14
msgid "Get the code"
msgstr "获取代码"
#: ..\..\..\templates/inc/footer.tpl:16
msgid "Based on"
msgstr "基于"
#: ..\..\..\templates/inc/header.tpl:4
msgid "Share on Twitter"
msgstr "分享到 Twitter"
#: ..\..\..\templates/inc/header.tpl:5
msgid "Share on Facebook"
msgstr "分享到 Facebook"
#: ..\..\..\templates/index.tpl:9
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
msgstr "在这里复制您的视频 Youtube、 Dailymotion 等) 的 URL"
#: ..\..\..\templates/index.tpl:19 ..\..\..\templates/password.tpl:10
#: ..\..\..\templates/playlist.tpl:23 ..\..\..\templates/video.tpl:85
#: ..\..\..\templates/video.tpl:90
msgid "Download"
msgstr "下载"
#: ..\..\..\templates/index.tpl:24
msgid "Audio only (MP3)"
msgstr "仅限音频mp3"
#: ..\..\..\templates/index.tpl:29
msgid "See all supported websites"
msgstr "请参阅支持的所有网站"
#: ..\..\..\templates/index.tpl:31
msgid "Drag this to your bookmarks bar:"
msgstr "把这个拖到你的书签:"
#: ..\..\..\templates/index.tpl:32
msgid "Bookmarklet"
msgstr "书签工具"
#: ..\..\..\templates/password.tpl:5
msgid "This video is protected"
msgstr "这个视频受保护"
#: ..\..\..\templates/password.tpl:6
msgid "You need a password in order to download this video."
msgstr "你需要密码才能下载这个视频。"
#: ..\..\..\templates/password.tpl:8
msgid "Video password"
msgstr "视频密码"
#: ..\..\..\templates/playlist.tpl:5
msgid "Videos extracted from the"
msgstr "视频从"
#: ..\..\..\templates/playlist.tpl:7
msgid "playlist:"
msgstr "播放列表提取:"
#: ..\..\..\templates/playlist.tpl:24
msgid "More options"
msgstr "更多选项"
#: ..\..\..\templates/video.tpl:6
msgid "You are going to download"
msgstr "你即将下载"
#: ..\..\..\templates/video.tpl:24
msgid "Available formats:"
msgstr "可用的格式︰"
#: ..\..\..\templates/video.tpl:31
msgid "Generic formats"
msgstr "通用格式"
#: ..\..\..\templates/video.tpl:34
msgid "Best"
msgstr "最佳"
#: ..\..\..\templates/video.tpl:39
msgid "Remux best video with best audio"
msgstr "重新封装最佳视频与最佳音频"
#: ..\..\..\templates/video.tpl:43
msgid "Worst"
msgstr "最差"
#: ..\..\..\templates/video.tpl:46
msgid "Detailed formats"
msgstr "详细格式"

View file

@ -3,6 +3,9 @@
require_once __DIR__.'/vendor/autoload.php'; require_once __DIR__.'/vendor/autoload.php';
use Alltube\Config; use Alltube\Config;
use Alltube\Controller\FrontController; use Alltube\Controller\FrontController;
use Alltube\LocaleManager;
use Alltube\LocaleMiddleware;
use Alltube\PlaylistArchiveStream;
use Alltube\UglyRouter; use Alltube\UglyRouter;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\App; use Slim\App;
@ -12,6 +15,8 @@ if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.ph
die; die;
} }
stream_wrapper_register('playlist', PlaylistArchiveStream::class);
$app = new App(); $app = new App();
$container = $app->getContainer(); $container = $app->getContainer();
$config = Config::getInstance(); $config = Config::getInstance();
@ -19,6 +24,8 @@ if ($config->uglyUrls) {
$container['router'] = new UglyRouter(); $container['router'] = new UglyRouter();
} }
$container['view'] = ViewFactory::create($container); $container['view'] = ViewFactory::create($container);
$container['locale'] = new LocaleManager($_COOKIE);
$app->add(new LocaleMiddleware($container));
$controller = new FrontController($container, null, $_COOKIE); $controller = new FrontController($container, null, $_COOKIE);
@ -40,4 +47,8 @@ $app->get(
'/redirect', '/redirect',
[$controller, 'redirect'] [$controller, 'redirect']
)->setName('redirect'); )->setName('redirect');
$app->get(
'/locale/{locale}',
[$controller, 'locale']
)->setName('locale');
$app->run(); $app->run();

View file

@ -33,10 +33,10 @@ var castModule = (function () {
function sessionListener(e) { function sessionListener(e) {
session = e; session = e;
session.addMediaListener(onMediaDiscovered.bind(this, 'addMediaListener')); session.addMediaListener(onMediaDiscovered);
session.addUpdateListener(updateListener.bind(this)); session.addUpdateListener(updateListener);
if (session.media.length !== 0) { if (session.media.length !== 0) {
onMediaDiscovered('onRequestSessionSuccess', session.media[0]); onMediaDiscovered();
} }
} }
@ -52,7 +52,7 @@ var castModule = (function () {
function onRequestSessionSuccess(e) { function onRequestSessionSuccess(e) {
session = 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); 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); session.loadMedia(request, onMediaDiscovered, onMediaError);
} }
function onLaunchError(e) { function onLaunchError(e) {
@ -80,7 +80,8 @@ var castModule = (function () {
} }
function initializeCastApi() { 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); 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); chrome.cast.initialize(apiConfig, onInitSuccess, onError);
} }
@ -103,4 +104,6 @@ var castModule = (function () {
}; };
}()); }());
window.addEventListener('load', castModule.init, false); if (typeof window === 'object') {
window.addEventListener('load', castModule.init, false);
}

1603
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,16 @@
{ {
"name": "alltube", "name": "alltube",
"description": "HTML GUI for youtube-dl", "description": "HTML GUI for youtube-dl",
"version": "0.9.0", "version": "0.10.0",
"author": "Pierre Rudloff", "author": "Pierre Rudloff",
"bugs": "https://github.com/Rudloff/alltube/issues", "bugs": "https://github.com/Rudloff/alltube/issues",
"dependencies": { "dependencies": {
"bower": "~1.8.0", "bower": "~1.8.0",
"grunt": "~1.0.1", "grunt": "~1.0.1",
"grunt-contrib-cssmin": "~2.0.0", "grunt-contrib-csslint": "~2.0.0",
"grunt-contrib-uglify": "~2.2.0" "grunt-contrib-cssmin": "~2.2.0",
"grunt-contrib-uglify": "~3.0.0",
"grunt-potomo": "~3.5.0"
}, },
"devDependencies": { "devDependencies": {
"grunt-contrib-compress": "~1.4.1", "grunt-contrib-compress": "~1.4.1",

View file

@ -9,7 +9,7 @@ You can ususally download the video by doing *File > Save to* or *ctrl + S*.
## How do I change config parameters? ## How do I change config parameters?
You need to create a YAML file called `config.yml` at the root of your project. You need to create a YAML file called `config.yml` in the `config/` folder.
Here are the parameters that you can set: Here are the parameters that you can set:
* `youtubedl`: path to your youtube-dl binary * `youtubedl`: path to your youtube-dl binary

View file

@ -1 +1 @@
Sitemap: http://alltubedownload.net/sitemap.xml Sitemap: http://alltubedownload.net/resources/sitemap.xml

View file

@ -2,8 +2,8 @@
<div class="wrapper"> <div class="wrapper">
<div class="main error"> <div class="main error">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<h2>An error occured</h2> <h2>{t}An error occured{/t}</h2>
Please check the URL of your video. {t}Please check the URL of your video.{/t}
<p><i> <p><i>
{foreach $errors as $error} {foreach $errors as $error}
{$error|escape} {$error|escape}

View file

@ -1,7 +1,7 @@
{include file='inc/head.tpl'} {include file='inc/head.tpl'}
{include file='inc/header.tpl'} {include file='inc/header.tpl'}
{include file='inc/logo.tpl'} {include file='inc/logo.tpl'}
<h2 class="titre">Supported websites</h2> <h2 class="titre">{t}Supported websites{/t}</h2>
<div class="tripleliste"> <div class="tripleliste">
<ul> <ul>
{foreach $extractors as $extractor} {foreach $extractors as $extractor}

View file

@ -1,19 +1,19 @@
</div> </div>
<footer> <footer class="small-font">
<div class="footer_wrapper"> <div class="footer_wrapper">
Code by <a rel="author" target="blank" {t}Code by{/t} <a rel="author" target="blank"
href="http://rudloff.pro/">Pierre Rudloff</a> href="http://rudloff.pro/">Pierre Rudloff</a>
&middot; Design by &middot; {t}Design by{/t}
<a rel="author" target="blank" <a rel="author" target="blank"
href="http://olivierhaquette.fr">Olivier Haquette</a> href="http://olivierhaquette.fr">Olivier Haquette</a>
&middot; &middot;
<a target="_blank" <a target="_blank"
href="https://www.facebook.com/pages/AllTube-Download/571380966249415" href="https://www.facebook.com/pages/AllTube-Download/571380966249415"
title="AllTube Download on Facebook">Like us on Facebook</a> title="{t}AllTube Download on Facebook{/t}">{t}Like us on Facebook{/t}</a>
&middot; &middot;
<a href="https://github.com/Rudloff/alltube">Get the code</a> <a href="https://github.com/Rudloff/alltube">{t}Get the code{/t}</a>
&middot; &middot;
Based on <a href="http://rg3.github.io/youtube-dl/">youtube-dl</a> {t}Based on{/t} <a href="http://rg3.github.io/youtube-dl/">youtube-dl</a>
</div> </div>
</footer> </footer>
<script src="{base_url}/dist/main.js"></script> <script src="{base_url}/dist/main.js"></script>

View file

@ -1,5 +1,6 @@
{locale path="../i18n" domain="Alltube"}
<!Doctype HTML> <!Doctype HTML>
<html lang="en"> <html {if isset($locale)}lang="{$locale->getBcp47()}"{/if}>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name=viewport content="width=device-width, initial-scale=1"> <meta name=viewport content="width=device-width, initial-scale=1">
@ -20,6 +21,6 @@
<meta name="twitter:creator" content="@Tael67" /> <meta name="twitter:creator" content="@Tael67" />
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script> <script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
<meta name="theme-color" content="#4F4F4F"> <meta name="theme-color" content="#4F4F4F">
<link rel="manifest" href="manifest.json" /> <link rel="manifest" href="{base_url}/resources/manifest.json" />
</head> </head>
<body class="{$class}"> <body class="{$class}">

View file

@ -1,8 +1,26 @@
<header> <header>
{if isset($supportedLocales)}
<div class="locales small-font">
<button class="localesBtn small-font" title="{t}Switch language{/t}">
{if isset($locale)}
{$locale->getCountry()->getEmoji()}
{else}
Set language
{/if}
</button>
<ul class="supportedLocales">
{foreach $supportedLocales as $supportedLocale}
{if $supportedLocale != $locale}
<li><a hreflang="{$supportedLocale->getBcp47()}" lang="{$supportedLocale->getBcp47()}" href="{path_for name='locale' data=['locale'=>$supportedLocale->getIso15897()]}">{$supportedLocale->getCountry()->getEmoji()} {$supportedLocale->getFullName()}</a></li>
{/if}
{/foreach}
</ul>
</div>
{/if}
<div class="social"> <div class="social">
<a class="twitter" href="http://twitter.com/home?status={base_url|urlencode}" target="_blank"> <a class="twitter" href="http://twitter.com/home?status={base_url|urlencode}" title="{t}Share on Twitter{/t}" target="_blank">
Share on Twitter<div class="twittermask"></div></a> <div class="twittermask"></div></a>
<a class="facebook" href="https://www.facebook.com/sharer/sharer.php?u={base_url|urlencode}" target="_blank">Share on Facebook<div class="facebookmask"></div></a> <a class="facebook" href="https://www.facebook.com/sharer/sharer.php?u={base_url|urlencode}" title="{t}Share on Facebook{/t}" target="_blank"><div class="facebookmask"></div></a>
</div> </div>
</header> </header>
<div class="wrapper"> <div class="wrapper">

View file

@ -5,30 +5,30 @@
alt="AllTube Download" width="328" height="284"></div> alt="AllTube Download" width="328" height="284"></div>
<form action="{path_for name="video"}"> <form action="{path_for name="video"}">
<label class="labelurl" for="url"> <label class="labelurl" for="url">
Copy here the URL of your video (Youtube, Dailymotion, etc.) {t}Copy here the URL of your video (Youtube, Dailymotion, etc.){/t}
</label> </label>
<div class="champs"> <div class="champs">
<span class="URLinput_wrapper"> <span class="URLinput_wrapper">
<input class="URLinput" type="url" name="url" id="url" <input class="URLinput large-font" type="url" name="url" id="url"
required autofocus placeholder="http://example.com/video" /> required autofocus placeholder="http://example.com/video" />
</span> </span>
{if $uglyUrls} {if $config->uglyUrls}
<input type="hidden" name="page" value="video" /> <input type="hidden" name="page" value="video" />
{/if} {/if}
<input class="downloadBtn" type="submit" value="Download" /><br/> <input class="downloadBtn large-font" type="submit" value="{t}Download{/t}" /><br/>
{if $convert} {if $config->convert}
<div class="mp3"> <div class="mp3 small-font">
<p><input type="checkbox" id="audio" class="audio" name="audio"> <p><input type="checkbox" id="audio" class="audio" name="audio">
<label for="audio"><span class="ui"></span> <label for="audio"><span class="ui"></span>
Audio only (MP3)</label></p> {t}Audio only (MP3){/t}</label></p>
</div> </div>
{/if} {/if}
</div> </div>
</form> </form>
<a class="combatiblelink" href="{path_for name="extractors"}">See all supported websites</a> <a class="combatiblelink small-font" href="{path_for name="extractors"}">{t}See all supported websites{/t}</a>
<div id="bookmarklet" class="bookmarklet_wrapper"> <div id="bookmarklet" class="bookmarklet_wrapper">
<p> Drag this to your bookmarks bar: </p> <p> {t}Drag this to your bookmarks bar:{/t} </p>
<a class="bookmarklet" href="javascript:window.location='{$domain}{path_for name='video'}?url='+encodeURIComponent(location.href);">Bookmarklet</a> <a class="bookmarklet small-font" href="javascript:window.location='{$domain}{path_for name='video'}?url='+encodeURIComponent(location.href);">{t}Bookmarklet{/t}</a>
</div> </div>
</div> </div>

View file

@ -2,12 +2,12 @@
<div class="wrapper"> <div class="wrapper">
<div class="main"> <div class="main">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<h2>This video is protected</h2> <h2>{t}This video is protected{/t}</h2>
<p>You need a password in order to download this video.</p> <p>{t}You need a password in order to download this video.{/t}</p>
<form action="" method="POST"> <form action="" method="POST">
<input class="URLinput" type="password" name="password" title="Video password" /> <input class="URLinput" type="password" name="password" title="{t}Video password{/t}" />
<br/><br/> <br/><br/>
<input class="downloadBtn" type="submit" value="Download" /> <input class="downloadBtn" type="submit" value="{t}Download{/t}" />
</form> </form>
</div> </div>
{include file='inc/footer.tpl'} {include file='inc/footer.tpl'}

View file

@ -2,13 +2,16 @@
<div class="wrapper"> <div class="wrapper">
<div class="main"> <div class="main">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<p>Videos extracted from the {if isset($video->title)}<i> <p>{t}Videos extracted from{/t} {if isset($video->title)}<i>
<a href="{$video->webpage_url}"> <a href="{$video->webpage_url}">
{$video->title}</a></i>{/if} playlist: {$video->title}</a></i>{/if}{t}:{/t}
</p> </p>
{if $config->stream}
<a href="{path_for name="redirect"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
{/if}
{foreach $video->entries as $video} {foreach $video->entries as $video}
<div class="playlist-entry"> <div class="playlist-entry">
<h3><a target="_blank" href="{strip} <h3 class="playlist-entry-title"><a target="_blank" href="{strip}
{if isset($video->ie_key) and $video->ie_key == Youtube and !filter_var($video->url, FILTER_VALIDATE_URL)} {if isset($video->ie_key) and $video->ie_key == Youtube and !filter_var($video->url, FILTER_VALIDATE_URL)}
https://www.youtube.com/watch?v= https://www.youtube.com/watch?v=
{/if} {/if}
@ -20,8 +23,8 @@
{$video->title} {$video->title}
{/if} {/if}
</a></h3> </a></h3>
<a target="_blank" class="downloadBtn" href="{path_for name="redirect"}?url={$video->url}">Download</a> <a target="_blank" class="downloadBtn" href="{path_for name="redirect"}?url={$video->url}">{t}Download{/t}</a>
<a target="_blank" href="{path_for name="video"}?url={$video->url}">More options</a> <a target="_blank" href="{path_for name="video"}?url={$video->url}">{t}More options{/t}</a>
</div> </div>
{/foreach} {/foreach}

View file

@ -3,7 +3,7 @@
<div itemscope itemtype="http://schema.org/VideoObject"> <div itemscope itemtype="http://schema.org/VideoObject">
<div class="main"> <div class="main">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<p id="download_intro">You are going to download<i itemprop="name"> <p id="download_intro">{t}You are going to download{/t}<i itemprop="name">
<a itemprop="url" id="video_link" <a itemprop="url" id="video_link"
data-ext="{$video->ext}" data-ext="{$video->ext}"
data-video="{$video->url|escape}" data-video="{$video->url|escape}"
@ -20,30 +20,30 @@
<meta itemprop="uploadDate" content="{$video->upload_date}" /> <meta itemprop="uploadDate" content="{$video->upload_date}" />
{/if} {/if}
<br/> <br/>
<form action="{path_for name="redirect"}">
<input type="hidden" name="url" value="{$video->webpage_url}" />
{if isset($video->formats)} {if isset($video->formats)}
<h3><label for="format">Available formats:</label></h3> <h3><label for="format">{t}Available formats:{/t}</label></h3>
<form action="{path_for name="redirect"}"> {if $config->uglyUrls}
<input type="hidden" name="url" value="{$video->webpage_url}" />
{if $uglyUrls}
<input type="hidden" name="page" value="redirect" /> <input type="hidden" name="page" value="redirect" />
{/if} {/if}
<select name="format" id="format" class="formats monospace"> <select name="format" id="format" class="formats monospace">
<optgroup label="Generic formats"> <optgroup label="{t}Generic formats{/t}">
<option value="best{$protocol}"> <option value="best{$protocol}">
{strip} {strip}
Best ({$video->ext}) {t}Best{/t} ({$video->ext})
{/strip} {/strip}
</option> </option>
{if $remux} {if $config->remux}
<option value="bestvideo+bestaudio"> <option value="bestvideo+bestaudio">
Remux best video with best audio {t}Remux best video with best audio{/t}
</option> </option>
{/if} {/if}
<option value="worst{$protocol}"> <option value="worst{$protocol}">
Worst {t}Worst{/t}
</option> </option>
</optgroup> </optgroup>
<optgroup label="Detailed formats" class="monospace"> <optgroup label="{t}Detailed formats{/t}" class="monospace">
{foreach $video->formats as $format} {foreach $video->formats as $format}
{if $config->stream || $format->protocol|in_array:array('http', 'https')} {if $config->stream || $format->protocol|in_array:array('http', 'https')}
{strip} {strip}
@ -82,12 +82,10 @@
{/foreach} {/foreach}
</optgroup> </optgroup>
</select><br/><br/> </select><br/><br/>
<input class="downloadBtn" type="submit" value="Download" /><br/> <input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/>
</form> </form>
{else} {else}
<input type="hidden" name="format" value="best{$protocol}" /> <input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/>
<a class="downloadBtn"
href="{$video->url|escape}">Download</a><br/>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -24,7 +24,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
*/ */
protected function setUp() protected function setUp()
{ {
$this->config = Config::getInstance('config_test.yml'); $this->config = Config::getInstance('config/config_test.yml');
} }
/** /**
@ -83,7 +83,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
Config::destroyInstance(); Config::destroyInstance();
putenv('CONVERT=1'); putenv('CONVERT=1');
putenv('PYTHON=foo'); putenv('PYTHON=foo');
$config = Config::getInstance('config_test.yml'); $config = Config::getInstance('config/config_test.yml');
$this->assertEquals($config->convert, true); $this->assertEquals($config->convert, true);
$this->assertEquals($config->python, 'foo'); $this->assertEquals($config->python, 'foo');
putenv('CONVERT'); putenv('CONVERT');

View file

@ -7,6 +7,7 @@ namespace Alltube\Test;
use Alltube\Config; use Alltube\Config;
use Alltube\Controller\FrontController; use Alltube\Controller\FrontController;
use Alltube\LocaleManager;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\Container; use Slim\Container;
use Slim\Http\Environment; use Slim\Http\Environment;
@ -55,7 +56,8 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
$this->request = Request::createFromEnvironment(Environment::mock()); $this->request = Request::createFromEnvironment(Environment::mock());
$this->response = new Response(); $this->response = new Response();
$this->container['view'] = ViewFactory::create($this->container, $this->request); $this->container['view'] = ViewFactory::create($this->container, $this->request);
$this->controller = new FrontController($this->container, Config::getInstance('config_test.yml')); $this->container['locale'] = new LocaleManager();
$this->controller = new FrontController($this->container, Config::getInstance('config/config_test.yml'));
$this->container['router']->map(['GET'], '/', [$this->controller, 'index']) $this->container['router']->map(['GET'], '/', [$this->controller, 'index'])
->setName('index'); ->setName('index');
$this->container['router']->map(['GET'], '/video', [$this->controller, 'video']) $this->container['router']->map(['GET'], '/video', [$this->controller, 'video'])
@ -64,6 +66,8 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
->setName('extractors'); ->setName('extractors');
$this->container['router']->map(['GET'], '/redirect', [$this->controller, 'redirect']) $this->container['router']->map(['GET'], '/redirect', [$this->controller, 'redirect'])
->setName('redirect'); ->setName('redirect');
$this->container['router']->map(['GET'], '/locale', [$this->controller, 'locale'])
->setName('locale');
} }
/** /**
@ -396,7 +400,7 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
{ {
$this->assertRequestIsOk( $this->assertRequestIsOk(
'redirect', 'redirect',
['url'=> 'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0'], ['url'=> 'http://www.canalc2.tv/video/12163', 'format'=>'rtmp'],
new Config(['stream'=>true]) new Config(['stream'=>true])
); );
} }
@ -453,4 +457,48 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
{ {
$this->assertRequestIsServerError('redirect', ['url'=>'http://example.com/foo']); $this->assertRequestIsServerError('redirect', ['url'=>'http://example.com/foo']);
} }
/**
* Test the redirect() function with an video that returns an empty URL.
* This can be caused by trying to redirect to a playlist.
*
* @return void
*/
public function testRedirectWithEmptyUrl()
{
$this->assertRequestIsServerError(
'redirect',
['url'=> 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
);
}
/**
* Test the redirect() function with a playlist stream.
*
* @return void
*/
public function testRedirectWithPlaylist()
{
$this->assertRequestIsOk(
'redirect',
['url'=> 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'],
new Config(['stream'=>true])
);
}
/**
* Test the locale() function.
*
* @return void
*/
public function testLocale()
{
$this->assertTrue(
$this->controller->locale(
$this->request,
$this->response,
['locale'=> 'fr_FR']
)->isRedirect()
);
}
} }

View file

@ -0,0 +1,77 @@
<?php
/**
* LocaleManagerTest class.
*/
namespace Alltube\Test;
use Alltube\Locale;
use Alltube\LocaleManager;
/**
* Unit tests for the Config class.
*/
class LocaleManagerTest extends \PHPUnit_Framework_TestCase
{
/**
* LocaleManager class instance.
*
* @var LocaleManager
*/
private $localeManager;
/**
* Prepare tests.
*/
protected function setUp()
{
$this->localeManager = new LocaleManager();
}
/**
* Test the getSupportedLocales function.
*
* @return void
*/
public function testConstructorWithCookies()
{
$_SESSION['Alltube\LocaleManager']['locale'] = 'foo_BAR';
$localeManager = new LocaleManager([]);
$this->assertEquals('foo_BAR', (string) $localeManager->getLocale());
}
/**
* Test the getSupportedLocales function.
*
* @return void
*/
public function testGetSupportedLocales()
{
foreach ($this->localeManager->getSupportedLocales() as $locale) {
$this->assertInstanceOf(Locale::class, $locale);
}
}
/**
* Test the getLocale function.
*
* @return void
*/
public function testGetLocale()
{
$this->assertNull($this->localeManager->getLocale());
}
/**
* Test the setLocale function.
*
* @return void
*/
public function testSetLocale()
{
$this->localeManager->setLocale(new Locale('foo_BAR'));
$locale = $this->localeManager->getLocale();
$this->assertInstanceOf(Locale::class, $locale);
$this->assertEquals('foo_BAR', (string) $locale);
}
}

View file

@ -0,0 +1,107 @@
<?php
/**
* LocaleMiddlewareTest class.
*/
namespace Alltube\Test;
use Alltube\LocaleManager;
use Alltube\LocaleMiddleware;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Unit tests for the FrontController class.
*/
class LocaleMiddlewareTest extends \PHPUnit_Framework_TestCase
{
/**
* LocaleMiddleware instance.
*
* @var LocaleMiddleware
*/
private $middleware;
/**
* Prepare tests.
*/
protected function setUp()
{
$container = new Container();
$container['locale'] = new LocaleManager();
$this->middleware = new LocaleMiddleware($container);
}
/**
* Test the testLocale() function.
*
* @return void
*/
public function testTestLocale()
{
$locale = [
'language'=> 'fr',
'region' => 'FR',
];
$this->assertEquals('fr_FR', $this->middleware->testLocale($locale));
}
/**
* Test the testLocale() function with an unsupported locale.
*
* @return void
*/
public function testLocaleWithWrongLocale()
{
$locale = [
'language'=> 'foo',
'region' => 'BAR',
];
$this->assertNull($this->middleware->testLocale($locale));
$this->assertNull($this->middleware->testLocale([]));
}
/**
* Test the __invoke() function.
*
* @return void
*/
public function testInvoke()
{
$request = Request::createFromEnvironment(Environment::mock());
$this->middleware->__invoke(
$request->withHeader('Accept-Language', 'fr-FR'),
new Response(),
function () {
}
);
}
/**
* Test the __invoke() function withot the Accept-Language header.
*
* @return void
*/
public function testInvokeWithoutHeader()
{
$request = Request::createFromEnvironment(Environment::mock());
$this->middleware->__invoke(
$request->withoutHeader('Accept-Language'),
new Response(),
function () {
}
);
}
/**
* Test that the environment is correctly set up.
*
* @return void
*/
public function testEnv()
{
$this->markTestIncomplete('We need to find a way to reliably test LC_ALL and LANG values');
}
}

79
tests/LocaleTest.php Normal file
View file

@ -0,0 +1,79 @@
<?php
/**
* LocaleTest class.
*/
namespace Alltube\Test;
use Alltube\Locale;
/**
* Unit tests for the Config class.
*/
class LocaleTest extends \PHPUnit_Framework_TestCase
{
/**
* Locale class instance.
*
* @var Locale
*/
private $locale;
/**
* Prepare tests.
*/
protected function setUp()
{
$this->locale = new Locale('fr_FR');
}
/**
* Test the __toString function.
*
* @return void
*/
public function testGetToString()
{
$this->assertEquals('fr_FR', $this->locale->__toString());
}
/**
* Test the getFullName function.
*
* @return void
*/
public function testGetFullName()
{
$this->assertEquals('français (France)', $this->locale->getFullName());
}
/**
* Test the getIso15897 function.
*
* @return void
*/
public function testGetIso15897()
{
$this->assertEquals('fr_FR', $this->locale->getIso15897());
}
/**
* Test the getBcp47 function.
*
* @return void
*/
public function testGetBcp47()
{
$this->assertEquals('fr-FR', $this->locale->getBcp47());
}
/**
* Test the getIso3166 function.
*
* @return void
*/
public function testGetIso3166()
{
$this->assertEquals('fr', $this->locale->getIso3166());
}
}

View file

@ -0,0 +1,105 @@
<?php
/**
* PlaylistArchiveStreamTest class.
*/
namespace Alltube\Test;
use Alltube\PlaylistArchiveStream;
/**
* Unit tests for the ViewFactory class.
*/
class PlaylistArchiveStreamTest extends \PHPUnit_Framework_TestCase
{
/**
* PlaylistArchiveStream instance.
*
* @var PlaylistArchiveStream
*/
private $stream;
/**
* Prepare tests.
*/
protected function setUp()
{
$this->stream = new PlaylistArchiveStream();
}
/**
* Test the stream_open() function.
*
* @return void
*/
public function testStreamOpen()
{
$this->assertTrue($this->stream->stream_open('playlist://foo'));
}
/**
* Test the stream_write() function.
*
* @return void
*/
public function testStreamWrite()
{
$this->assertEquals(0, $this->stream->stream_write());
}
/**
* Test the stream_stat() function.
*
* @return void
*/
public function testStreamStat()
{
$this->assertEquals(['mode'=>4096], $this->stream->stream_stat());
}
/**
* Test the stream_tell() function.
*
* @return void
*/
public function testStreamTell()
{
$this->stream->stream_open('playlist://foo');
$this->assertInternalType('int', $this->stream->stream_tell());
}
/**
* Test the stream_seek() function.
*
* @return void
*/
public function testStreamSeek()
{
$this->stream->stream_open('playlist://foo');
$this->assertInternalType('bool', $this->stream->stream_seek(3));
}
/**
* Test the stream_read() function.
*
* @return void
*/
public function testStreamRead()
{
$this->stream->stream_open('playlist://BaW_jenozKc;BaW_jenozKc/worst');
while (!$this->stream->stream_eof()) {
$this->assertLessThanOrEqual(8192, strlen($this->stream->stream_read(8192)));
}
}
/**
* Test the stream_eof() function.
*
* @return void
*/
public function testStreamEof()
{
$this->stream->stream_open('playlist://foo');
$this->assertFalse($this->stream->stream_eof());
}
}

View file

@ -25,7 +25,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
*/ */
protected function setUp() protected function setUp()
{ {
$this->download = new VideoDownload(Config::getInstance('config_test.yml')); $this->download = new VideoDownload(Config::getInstance('config/config_test.yml'));
} }
/** /**
@ -151,32 +151,32 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
return [ return [
[ [
'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'best[protocol^=http]', 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'best[protocol^=http]',
"It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU", 'It_s_Not_Me_It_s_You_-_Hearts_Under_Fire-M7IpKCZ47pU',
'mp4', 'mp4',
'googlevideo.com', 'googlevideo.com',
], ],
[ [
'https://www.youtube.com/watch?v=RJJ6FCAXvKg', 22, 'https://www.youtube.com/watch?v=RJJ6FCAXvKg', 22,
"'Heart Attack' - Demi Lovato ". 'Heart_Attack_-_Demi_Lovato_'.
'(Sam Tsui & Against The Current)-RJJ6FCAXvKg', 'Sam_Tsui_Against_The_Current-RJJ6FCAXvKg',
'mp4', 'mp4',
'googlevideo.com', 'googlevideo.com',
], ],
[ [
'https://vimeo.com/24195442', 'best[protocol^=http]', 'https://vimeo.com/24195442', 'best[protocol^=http]',
'Carving the Mountains-24195442', 'Carving_the_Mountains-24195442',
'mp4', 'mp4',
'vimeocdn.com', 'vimeocdn.com',
], ],
[ [
'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best', 'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
'Leonard Cohen, Kaleidoscope - BBC Radio 4-b039d07m', 'Leonard_Cohen_Kaleidoscope_-_BBC_Radio_4-b039d07m',
'flv', 'flv',
'bbcodspdns.fcod.llnwd.net', 'bbcodspdns.fcod.llnwd.net',
], ],
[ [
'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0', 'bestaudio/best', 'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0', 'bestaudio/best',
'GRIP sucht den Sommerkönig-folge-203-0', 'GRIP_sucht_den_Sommerkonig-folge-203-0',
'f4v', 'f4v',
'edgefcs.net', 'edgefcs.net',
], ],
@ -193,7 +193,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
return [ return [
[ [
'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'bestvideo+bestaudio', 'https://www.youtube.com/watch?v=M7IpKCZ47pU', 'bestvideo+bestaudio',
"It's Not Me, It's You - Hearts Under Fire-M7IpKCZ47pU", 'It_s_Not_Me_It_s_You_-_Hearts_Under_Fire-M7IpKCZ47pU',
'mp4', 'mp4',
'googlevideo.com', 'googlevideo.com',
], ],
@ -210,7 +210,7 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
return [ return [
[ [
'https://twitter.com/verge/status/813055465324056576/video/1', 'best', 'https://twitter.com/verge/status/813055465324056576/video/1', 'best',
'The Verge - This tiny origami robot can self-fold and complete tasks-813055465324056576', 'The_Verge_-_This_tiny_origami_robot_can_self-fold_and_complete_tasks-813055465324056576',
'mp4', 'mp4',
'video.twimg.com', 'video.twimg.com',
], ],
@ -226,10 +226,10 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
{ {
return [ return [
[ [
'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0', 'bestaudio/best', 'http://www.canalc2.tv/video/12163', 'rtmp',
'GRIP sucht den Sommerkönig-folge-203-0', 'Terrasses_du_Numerique-12163',
'f4v', 'flv',
'edgefcs.net', 'vod-flash.u-strasbg.fr',
], ],
]; ];
} }
@ -480,4 +480,18 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
$video = $download->getJSON($url, $format); $video = $download->getJSON($url, $format);
$download->getM3uStream($video); $download->getM3uStream($video);
} }
/**
* Test getPlaylistArchiveStream function without avconv.
*
* @return void
*/
public function testGetPlaylistArchiveStream()
{
$video = $this->download->getJSON(
'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC',
'best'
);
$this->assertStream($this->download->getPlaylistArchiveStream($video, 'best'));
}
} }

View file

@ -7,6 +7,8 @@ namespace Alltube\Test;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\Container; use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Views\Smarty; use Slim\Views\Smarty;
/** /**
@ -24,4 +26,16 @@ class ViewFactoryTest extends \PHPUnit_Framework_TestCase
$view = ViewFactory::create(new Container()); $view = ViewFactory::create(new Container());
$this->assertInstanceOf(Smarty::class, $view); $this->assertInstanceOf(Smarty::class, $view);
} }
/**
* Test the create() function with a X-Forwarded-Proto header.
*
* @return void
*/
public function testCreateWithXForwardedProto()
{
$request = Request::createFromEnvironment(Environment::mock());
$view = ViewFactory::create(new Container(), $request->withHeader('X-Forwarded-Proto', 'https'));
$this->assertInstanceOf(Smarty::class, $view);
}
} }

View file

@ -2,6 +2,7 @@
/** /**
* File used to bootstrap tests. * File used to bootstrap tests.
*/ */
use Alltube\PlaylistArchiveStream;
/** /**
* Composer autoload. * Composer autoload.
@ -9,3 +10,5 @@
require_once __DIR__.'/../vendor/autoload.php'; require_once __DIR__.'/../vendor/autoload.php';
session_start(); session_start();
stream_wrapper_register('playlist', PlaylistArchiveStream::class);