Merge branch 'release/2.1.0'

This commit is contained in:
Pierre Rudloff 2019-11-29 22:37:19 +01:00
commit d5fcb3475d
39 changed files with 1023 additions and 617 deletions

View file

@ -34,5 +34,5 @@ FileETag None
Header set X-Content-Type-Options nosniff Header set X-Content-Type-Options nosniff
Header set X-XSS-Protection "1; mode=block" Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy no-referrer Header set Referrer-Policy no-referrer
Header set Content-Security-Policy "default-src 'self'; object-src 'none'; script-src 'none'; img-src http:" Header set Content-Security-Policy "default-src 'self'; object-src 'none'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src http:"
</ifmodule> </ifmodule>

View file

@ -101,7 +101,7 @@ module.exports = function (grunt) {
'i18n/es_ES/LC_MESSAGES/Alltube.mo': 'i18n/es_ES/LC_MESSAGES/Alltube.po', 'i18n/es_ES/LC_MESSAGES/Alltube.mo': 'i18n/es_ES/LC_MESSAGES/Alltube.po',
'i18n/de_DE/LC_MESSAGES/Alltube.mo': 'i18n/de_DE/LC_MESSAGES/Alltube.po', 'i18n/de_DE/LC_MESSAGES/Alltube.mo': 'i18n/de_DE/LC_MESSAGES/Alltube.po',
'i18n/pt_BR/LC_MESSAGES/Alltube.mo': 'i18n/pt_BR/LC_MESSAGES/Alltube.po', 'i18n/pt_BR/LC_MESSAGES/Alltube.mo': 'i18n/pt_BR/LC_MESSAGES/Alltube.po',
'i18n/ar_001/LC_MESSAGES/Alltube.mo': 'i18n/ar_001/LC_MESSAGES/Alltube.po' 'i18n/ar/LC_MESSAGES/Alltube.mo': 'i18n/ar/LC_MESSAGES/Alltube.po'
} }
} }
}, },

View file

@ -17,14 +17,14 @@
{ {
"url": "https://github.com/piotras/heroku-buildpack-gettext.git" "url": "https://github.com/piotras/heroku-buildpack-gettext.git"
}, },
{
"url": "heroku/php"
},
{ {
"url": "heroku/nodejs" "url": "heroku/nodejs"
}, },
{ {
"url": "heroku/python" "url": "heroku/python"
},
{
"url": "heroku/php"
} }
], ],
"env": { "env": {

View file

@ -8,6 +8,7 @@ namespace Alltube;
use Exception; use Exception;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Jawira\CaseConverter\Convert;
/** /**
* Manage config parameters. * Manage config parameters.
@ -134,6 +135,13 @@ class Config
*/ */
public $genericFormats = []; public $genericFormats = [];
/**
* Enable debug mode.
*
* @var bool
*/
public $debug = false;
/** /**
* Config constructor. * Config constructor.
* *
@ -222,17 +230,18 @@ class Config
/** /**
* Override options from environement variables. * Override options from environement variables.
* Supported environment variables: CONVERT, PYTHON, AUDIO_BITRATE. * Environment variables should use screaming snake case: CONVERT, PYTHON, AUDIO_BITRATE, etc.
* If the value is an array, you should use the YAML format: "CONVERT_ADVANCED_FORMATS='[foo, bar]'"
* *
* @return void * @return void
*/ */
private function getEnv() private function getEnv()
{ {
foreach (['CONVERT', 'PYTHON', 'AUDIO_BITRATE', 'STREAM'] as $var) { foreach (get_object_vars($this) as $prop => $value) {
$env = getenv($var); $convert = new Convert($prop);
$env = getenv($convert->toSnake(true));
if ($env) { if ($env) {
$prop = lcfirst(str_replace('_', '', ucwords(strtolower($var), '_'))); $this->$prop = Yaml::parse($env);
$this->$prop = $env;
} }
} }
} }

View file

@ -38,8 +38,10 @@ class Locale
{ {
$parse = AcceptLanguage::parse($locale); $parse = AcceptLanguage::parse($locale);
$this->language = $parse[1]['language']; $this->language = $parse[1]['language'];
if (!empty($parse[1]['region'])) {
$this->region = $parse[1]['region']; $this->region = $parse[1]['region'];
} }
}
/** /**
* Convert the locale to a string. * Convert the locale to a string.
@ -68,7 +70,11 @@ class Locale
*/ */
public function getIso15897() public function getIso15897()
{ {
if (isset($this->region)) {
return $this->language . '_' . $this->region; return $this->language . '_' . $this->region;
} else {
return $this->language;
}
} }
/** /**
@ -78,7 +84,11 @@ class Locale
*/ */
public function getBcp47() public function getBcp47()
{ {
if (isset($this->region)) {
return $this->language . '-' . $this->region; return $this->language . '-' . $this->region;
} else {
return $this->language;
}
} }
/** /**
@ -98,6 +108,8 @@ class Locale
*/ */
public function getCountry() public function getCountry()
{ {
if (isset($this->region)) {
return country($this->getIso3166()); return country($this->getIso3166());
} }
}
} }

View file

@ -8,6 +8,8 @@ namespace Alltube;
use Aura\Session\Segment; use Aura\Session\Segment;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\MoFileLoader;
/** /**
* Class used to manage locales. * Class used to manage locales.
@ -19,7 +21,7 @@ class LocaleManager
* *
* @var array * @var array
*/ */
private $supportedLocales = ['en_US', 'fr_FR', 'zh_CN', 'es_ES', 'pt_BR', 'de_DE', 'ar_001']; private $supportedLocales = ['en_US', 'fr_FR', 'zh_CN', 'es_ES', 'pt_BR', 'de_DE', 'ar'];
/** /**
* Current locale. * Current locale.
@ -35,19 +37,49 @@ class LocaleManager
*/ */
private $sessionSegment; private $sessionSegment;
/**
* Default locale.
*
* @var string
*/
private const DEFAULT_LOCALE = 'en';
/**
* Symfony Translator instance.
*
* @var Translator
*/
private $translator;
/**
* Singleton instance.
*
* @var LocaleManager|null
*/
private static $instance;
/** /**
* LocaleManager constructor. * LocaleManager constructor.
*/ */
public function __construct() private function __construct()
{ {
$session = SessionManager::getSession(); $session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class); $this->sessionSegment = $session->getSegment(self::class);
$cookieLocale = $this->sessionSegment->get('locale'); $cookieLocale = $this->sessionSegment->get('locale');
$this->translator = new Translator(self::DEFAULT_LOCALE);
if (isset($cookieLocale)) { if (isset($cookieLocale)) {
$this->setLocale(new Locale($cookieLocale)); $this->setLocale(new Locale($cookieLocale));
} }
bindtextdomain('Alltube', __DIR__ . '/../i18n/');
textdomain('Alltube'); $this->translator->addLoader('gettext', new MoFileLoader());
foreach ($this->getSupportedLocales() as $locale) {
$this->translator->addResource(
'gettext',
__DIR__ . '/../i18n/' . $locale->getIso15897() . '/LC_MESSAGES/Alltube.mo',
$locale->getIso15897()
);
}
} }
/** /**
@ -58,17 +90,10 @@ class LocaleManager
public function getSupportedLocales() public function getSupportedLocales()
{ {
$return = []; $return = [];
$process = new Process(['locale', '-a']);
$process->run();
$installedLocales = explode(PHP_EOL, trim($process->getOutput()));
foreach ($this->supportedLocales as $supportedLocale) { foreach ($this->supportedLocales as $supportedLocale) {
if (
in_array($supportedLocale, $installedLocales)
|| in_array($supportedLocale . '.utf8', $installedLocales)
) {
$return[] = new Locale($supportedLocale); $return[] = new Locale($supportedLocale);
} }
}
return $return; return $return;
} }
@ -90,8 +115,7 @@ class LocaleManager
*/ */
public function setLocale(Locale $locale) public function setLocale(Locale $locale)
{ {
putenv('LANG=' . $locale); $this->translator->setLocale($locale->getIso15897());
setlocale(LC_ALL, [$locale . '.utf8', $locale]);
$this->curLocale = $locale; $this->curLocale = $locale;
$this->sessionSegment->set('locale', $locale); $this->sessionSegment->set('locale', $locale);
} }
@ -101,7 +125,61 @@ class LocaleManager
*/ */
public function unsetLocale() public function unsetLocale()
{ {
$this->translator->setLocale(self::DEFAULT_LOCALE);
$this->curLocale = null; $this->curLocale = null;
$this->sessionSegment->clear(); $this->sessionSegment->clear();
} }
/**
* Smarty "t" block.
*
* @param array $params Block parameters
* @param string $text Block content
*
* @return string Translated string
*/
public function smartyTranslate(array $params, $text)
{
if (isset($params['params'])) {
return $this->t($text, $params['params']);
} else {
return $this->t($text);
}
}
/**
* Translate a string.
*
* @param string $string String to translate
*
* @return string Translated string
*/
public function t($string, array $params = [])
{
return $this->translator->trans($string, $params);
}
/**
* Get LocaleManager singleton instance.
*
* @return LocaleManager
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Destroy singleton instance.
*
* @return void
*/
public static function destroyInstance()
{
self::$instance = null;
}
} }

View file

@ -74,6 +74,13 @@ class Video
*/ */
private $urls; private $urls;
/**
* LocaleManager instance.
*
* @var LocaleManager
*/
protected $localeManager;
/** /**
* VideoDownload constructor. * VideoDownload constructor.
* *
@ -87,6 +94,8 @@ class Video
$this->requestedFormat = $requestedFormat; $this->requestedFormat = $requestedFormat;
$this->password = $password; $this->password = $password;
$this->config = Config::getInstance(); $this->config = Config::getInstance();
$this->localeManager = LocaleManager::getInstance();
} }
/** /**
@ -116,7 +125,9 @@ class Video
* */ * */
public static function getExtractors() public static function getExtractors()
{ {
return explode("\n", trim(self::callYoutubedl(['--list-extractors']))); $video = new self('');
return explode("\n", trim($video->callYoutubedl(['--list-extractors'])));
} }
/** /**
@ -130,7 +141,7 @@ class Video
* *
* @return string Result * @return string Result
*/ */
private static function callYoutubedl(array $arguments) private function callYoutubedl(array $arguments)
{ {
$config = Config::getInstance(); $config = Config::getInstance();
@ -145,7 +156,7 @@ class Video
if ($errorOutput == 'ERROR: This video is protected by a password, use the --video-password option') { if ($errorOutput == 'ERROR: This video is protected by a password, use the --video-password option') {
throw new PasswordException($errorOutput, $exitCode); throw new PasswordException($errorOutput, $exitCode);
} elseif (substr($errorOutput, 0, 21) == 'ERROR: Wrong password') { } elseif (substr($errorOutput, 0, 21) == 'ERROR: Wrong password') {
throw new Exception(_('Wrong password'), $exitCode); throw new Exception($this->localeManager->t('Wrong password'), $exitCode);
} else { } else {
throw new Exception($errorOutput, $exitCode); throw new Exception($errorOutput, $exitCode);
} }
@ -177,7 +188,7 @@ class Video
$arguments[] = $this->password; $arguments[] = $this->password;
} }
return $this::callYoutubedl($arguments); return $this->callYoutubedl($arguments);
} }
/** /**
@ -236,7 +247,7 @@ class Video
$this->urls = explode("\n", $this->getProp('get-url')); $this->urls = explode("\n", $this->getProp('get-url'));
if (empty($this->urls[0])) { if (empty($this->urls[0])) {
throw new EmptyUrlException(_('youtube-dl returned an empty URL.')); throw new EmptyUrlException($this->localeManager->t('youtube-dl returned an empty URL.'));
} }
} }
@ -345,7 +356,12 @@ class Video
$to = null $to = null
) { ) {
if (!$this->checkCommand([$this->config->avconv, '-version'])) { if (!$this->checkCommand([$this->config->avconv, '-version'])) {
throw new Exception(_('Can\'t find avconv or ffmpeg at ') . $this->config->avconv . '.'); throw new Exception(
$this->localeManager->t(
"Can't find avconv or ffmpeg at @path.",
['@path' => $this->config->avconv]
)
);
} }
$durationRegex = '/(\d+:)?(\d+:)?(\d+)/'; $durationRegex = '/(\d+:)?(\d+:)?(\d+)/';
@ -358,14 +374,14 @@ class Video
if (!empty($from)) { if (!empty($from)) {
if (!preg_match($durationRegex, $from)) { if (!preg_match($durationRegex, $from)) {
throw new Exception(_('Invalid start time: ') . $from . '.'); throw new Exception($this->localeManager->t('Invalid start time: @from.', ['@from' => $from]));
} }
$afterArguments[] = '-ss'; $afterArguments[] = '-ss';
$afterArguments[] = $from; $afterArguments[] = $from;
} }
if (!empty($to)) { if (!empty($to)) {
if (!preg_match($durationRegex, $to)) { if (!preg_match($durationRegex, $to)) {
throw new Exception(_('Invalid end time: ') . $to . '.'); throw new Exception($this->localeManager->t('Invalid end time: @to.', ['@to' => $to]));
} }
$afterArguments[] = '-to'; $afterArguments[] = '-to';
$afterArguments[] = $to; $afterArguments[] = $to;
@ -411,14 +427,14 @@ class Video
public function getAudioStream($from = null, $to = null) public function getAudioStream($from = null, $to = null)
{ {
if (isset($this->_type) && $this->_type == 'playlist') { if (isset($this->_type) && $this->_type == 'playlist') {
throw new Exception(_('Conversion of playlists is not supported.')); throw new Exception($this->localeManager->t('Conversion of playlists is not supported.'));
} }
if (isset($this->protocol)) { if (isset($this->protocol)) {
if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) {
throw new Exception(_('Conversion of M3U8 files is not supported.')); throw new Exception($this->localeManager->t('Conversion of M3U8 files is not supported.'));
} elseif ($this->protocol == 'http_dash_segments') { } elseif ($this->protocol == 'http_dash_segments') {
throw new Exception(_('Conversion of DASH segments is not supported.')); throw new Exception($this->localeManager->t('Conversion of DASH segments is not supported.'));
} }
} }
@ -427,7 +443,7 @@ class Video
$stream = popen($avconvProc->getCommandLine(), 'r'); $stream = popen($avconvProc->getCommandLine(), 'r');
if (!is_resource($stream)) { if (!is_resource($stream)) {
throw new Exception(_('Could not open popen stream.')); throw new Exception($this->localeManager->t('Could not open popen stream.'));
} }
return $stream; return $stream;
@ -444,7 +460,12 @@ class Video
public function getM3uStream() public function getM3uStream()
{ {
if (!$this->checkCommand([$this->config->avconv, '-version'])) { if (!$this->checkCommand([$this->config->avconv, '-version'])) {
throw new Exception(_('Can\'t find avconv or ffmpeg at ') . $this->config->avconv . '.'); throw new Exception(
$this->localeManager->t(
"Can't find avconv or ffmpeg at @path.",
['@path' => $this->config->avconv]
)
);
} }
$urls = $this->getUrl(); $urls = $this->getUrl();
@ -464,7 +485,7 @@ class Video
$stream = popen($process->getCommandLine(), 'r'); $stream = popen($process->getCommandLine(), 'r');
if (!is_resource($stream)) { if (!is_resource($stream)) {
throw new Exception(_('Could not open popen stream.')); throw new Exception($this->localeManager->t('Could not open popen stream.'));
} }
return $stream; return $stream;
@ -482,7 +503,7 @@ class Video
$urls = $this->getUrl(); $urls = $this->getUrl();
if (!isset($urls[0]) || !isset($urls[1])) { if (!isset($urls[0]) || !isset($urls[1])) {
throw new Exception(_('This video does not have two URLs.')); throw new Exception($this->localeManager->t('This video does not have two URLs.'));
} }
$process = new Process( $process = new Process(
@ -501,7 +522,7 @@ class Video
$stream = popen($process->getCommandLine(), 'r'); $stream = popen($process->getCommandLine(), 'r');
if (!is_resource($stream)) { if (!is_resource($stream)) {
throw new Exception(_('Could not open popen stream.')); throw new Exception($this->localeManager->t('Could not open popen stream.'));
} }
return $stream; return $stream;
@ -534,7 +555,7 @@ class Video
); );
$stream = popen($process->getCommandLine(), 'r'); $stream = popen($process->getCommandLine(), 'r');
if (!is_resource($stream)) { if (!is_resource($stream)) {
throw new Exception(_('Could not open popen stream.')); throw new Exception($this->localeManager->t('Could not open popen stream.'));
} }
return $stream; return $stream;
@ -554,7 +575,7 @@ class Video
public function getConvertedStream($audioBitrate, $filetype) public function getConvertedStream($audioBitrate, $filetype)
{ {
if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) {
throw new Exception(_('Conversion of M3U8 files is not supported.')); throw new Exception($this->localeManager->t('Conversion of M3U8 files is not supported.'));
} }
$avconvProc = $this->getAvconvProcess($audioBitrate, $filetype, false); $avconvProc = $this->getAvconvProcess($audioBitrate, $filetype, false);
@ -562,7 +583,7 @@ class Video
$stream = popen($avconvProc->getCommandLine(), 'r'); $stream = popen($avconvProc->getCommandLine(), 'r');
if (!is_resource($stream)) { if (!is_resource($stream)) {
throw new Exception(_('Could not open popen stream.')); throw new Exception($this->localeManager->t('Could not open popen stream.'));
} }
return $stream; return $stream;
@ -592,6 +613,13 @@ class Video
$client = new Client(); $client = new Client();
$urls = $this->getUrl(); $urls = $this->getUrl();
return $client->request('GET', $urls[0], ['stream' => true, 'headers' => $headers]); return $client->request(
'GET',
$urls[0],
[
'stream' => true,
'headers' => array_merge((array) $this->http_headers, $headers)
]
);
} }
} }

View file

@ -35,9 +35,12 @@ class ViewFactory
$request = $request->withUri($request->getUri()->withScheme('https')->withPort(443)); $request = $request->withUri($request->getUri()->withScheme('https')->withPort(443));
} }
$localeManager = $container['locale'];
$smartyPlugins = new SmartyPlugins($container['router'], $request->getUri()->withUserInfo(null)); $smartyPlugins = new SmartyPlugins($container['router'], $request->getUri()->withUserInfo(null));
$view->registerPlugin('function', 'path_for', [$smartyPlugins, 'pathFor']); $view->registerPlugin('function', 'path_for', [$smartyPlugins, 'pathFor']);
$view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']); $view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);
$view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);
return $view; return $view;
} }

View file

@ -12,11 +12,12 @@
"guzzlehttp/guzzle": "~6.3.0", "guzzlehttp/guzzle": "~6.3.0",
"aura/session": "~2.1.0", "aura/session": "~2.1.0",
"barracudanetworks/archivestream-php": "~1.0.5", "barracudanetworks/archivestream-php": "~1.0.5",
"smarty-gettext/smarty-gettext": "~1.6.0",
"zonuexe/http-accept-language": "~0.4.1", "zonuexe/http-accept-language": "~0.4.1",
"rinvex/countries": "~3.1.0", "rinvex/countries": "~3.1.0",
"php-mock/php-mock-mockery": "~1.3.0", "php-mock/php-mock-mockery": "~1.3.0",
"ext-xsl": "*" "ext-xsl": "*",
"jawira/case-converter": "^1.2",
"symfony/translation": "^3.4"
}, },
"require-dev": { "require-dev": {
"symfony/var-dumper": "~3.4.1", "symfony/var-dumper": "~3.4.1",
@ -27,7 +28,8 @@
"heroku/heroku-buildpack-php": "^162.0", "heroku/heroku-buildpack-php": "^162.0",
"anam/phantomjs-linux-x86-binary": "~2.1.1", "anam/phantomjs-linux-x86-binary": "~2.1.1",
"phpstan/phpstan": "~0.9.2", "phpstan/phpstan": "~0.9.2",
"roave/security-advisories": "dev-master" "roave/security-advisories": "dev-master",
"smarty-gettext/smarty-gettext": "^1.6"
}, },
"extra": { "extra": {
"paas": { "paas": {
@ -90,7 +92,7 @@
"compile": "composer install --ignore-platform-reqs", "compile": "composer install --ignore-platform-reqs",
"update-locales": [ "update-locales": [
"tsmarty2c.php templates > i18n/template.pot", "tsmarty2c.php templates > i18n/template.pot",
"xgettext --omit-header -j -o i18n/template.pot classes/* controllers/*" "xgettext --omit-header -kt -j -o i18n/template.pot classes/*.php classes/*/*.php controllers/*"
], ],
"youtube-dl": "vendor/rg3/youtube-dl/youtube_dl/__main__.py" "youtube-dl": "vendor/rg3/youtube-dl/youtube_dl/__main__.py"
}, },

345
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "07b922cb69b4f4dbd5e537656d559c8d", "content-hash": "c773a24c670cb4c431217ea94f03b942",
"packages": [ "packages": [
{ {
"name": "aura/session", "name": "aura/session",
@ -137,6 +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",
"abandoned": "psr/container",
"time": "2017-02-14T19:40:03+00:00" "time": "2017-02-14T19:40:03+00:00"
}, },
{ {
@ -374,6 +375,50 @@
], ],
"time": "2016-01-20T08:20:44+00:00" "time": "2016-01-20T08:20:44+00:00"
}, },
{
"name": "jawira/case-converter",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/jawira/case-converter.git",
"reference": "79716629a298e44507a8eed9b997968f39367abc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jawira/case-converter/zipball/79716629a298e44507a8eed9b997968f39367abc",
"reference": "79716629a298e44507a8eed9b997968f39367abc",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.4"
},
"suggest": {
"pds/skeleton": "PHP Package Development Standards"
},
"type": "library",
"autoload": {
"psr-4": {
"Jawira\\CaseConverter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jawira Portugal"
}
],
"description": "Convert string between **Camel Case** ?, **Snake Case** ?and **Kebab Case** ?.",
"keywords": [
"camel case",
"kebab case",
"snake case"
],
"time": "2019-03-18T05:59:08+00:00"
},
{ {
"name": "mathmarques/smarty-view", "name": "mathmarques/smarty-view",
"version": "1.1.2", "version": "1.1.2",
@ -1078,61 +1123,6 @@
], ],
"time": "2019-04-16T16:47:29+00:00" "time": "2019-04-16T16:47:29+00:00"
}, },
{
"name": "smarty-gettext/smarty-gettext",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/smarty-gettext/smarty-gettext.git",
"reference": "9a7d9284b5374caeae5eb226cf486efb4bc748b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smarty-gettext/smarty-gettext/zipball/9a7d9284b5374caeae5eb226cf486efb4bc748b4",
"reference": "9a7d9284b5374caeae5eb226cf486efb4bc748b4",
"shasum": ""
},
"require": {
"ext-gettext": "*",
"ext-pcre": "*",
"php": "~5.3|~7.0"
},
"require-dev": {
"azatoth/php-pgettext": "~1.0",
"phpunit/phpunit": ">=4.8.36",
"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": "2019-01-17T23:06:53+00:00"
},
{ {
"name": "smarty/smarty", "name": "smarty/smarty",
"version": "v3.1.33", "version": "v3.1.33",
@ -1244,6 +1234,65 @@
], ],
"time": "2019-02-06T07:57:58+00:00" "time": "2019-02-06T07:57:58+00:00"
}, },
{
"name": "symfony/polyfill-mbstring",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
},
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v3.4.29", "version": "v3.4.29",
@ -1293,6 +1342,76 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-05-30T15:47:52+00:00" "time": "2019-05-30T15:47:52+00:00"
}, },
{
"name": "symfony/translation",
"version": "v3.4.35",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "2031c895bc97ac1787d418d90bd1ed7d299f2772"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/2031c895bc97ac1787d418d90bd1ed7d299f2772",
"reference": "2031c895bc97ac1787d418d90bd1ed7d299f2772",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/config": "<2.8",
"symfony/dependency-injection": "<3.4",
"symfony/yaml": "<3.4"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.8|~3.0|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/intl": "^2.8.18|^3.2.5|~4.0",
"symfony/var-dumper": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0"
},
"suggest": {
"psr/log-implementation": "To use logging capability in translator",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Translation\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2019-10-30T12:43:22+00:00"
},
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v3.4.29", "version": "v3.4.29",
@ -3829,6 +3948,61 @@
"homepage": "https://github.com/sebastianbergmann/version", "homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03T07:35:21+00:00" "time": "2016-10-03T07:35:21+00:00"
}, },
{
"name": "smarty-gettext/smarty-gettext",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/smarty-gettext/smarty-gettext.git",
"reference": "9a7d9284b5374caeae5eb226cf486efb4bc748b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smarty-gettext/smarty-gettext/zipball/9a7d9284b5374caeae5eb226cf486efb4bc748b4",
"reference": "9a7d9284b5374caeae5eb226cf486efb4bc748b4",
"shasum": ""
},
"require": {
"ext-gettext": "*",
"ext-pcre": "*",
"php": "~5.3|~7.0"
},
"require-dev": {
"azatoth/php-pgettext": "~1.0",
"phpunit/phpunit": ">=4.8.36",
"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": "2019-01-17T23:06:53+00:00"
},
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
"version": "3.5.0", "version": "3.5.0",
@ -4057,65 +4231,6 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-05-30T15:47:52+00:00" "time": "2019-05-30T15:47:52+00:00"
}, },
{
"name": "symfony/polyfill-mbstring",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
},
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
"version": "v3.4.29", "version": "v3.4.29",

View file

@ -51,3 +51,6 @@ genericFormats:
best: Best best: Best
bestvideo+bestaudio: Remux best video with best audio bestvideo+bestaudio: Remux best video with best audio
worst: Worst worst: Worst
# Enable debug mode.
debug: false

View file

@ -7,6 +7,7 @@
namespace Alltube\Controller; namespace Alltube\Controller;
use Alltube\Config; use Alltube\Config;
use Alltube\LocaleManager;
use Alltube\SessionManager; use Alltube\SessionManager;
use Alltube\Video; use Alltube\Video;
use Aura\Session\Segment; use Aura\Session\Segment;
@ -53,6 +54,13 @@ abstract class BaseController
*/ */
protected $sessionSegment; protected $sessionSegment;
/**
* LocaleManager instance.
*
* @var LocaleManager
*/
protected $localeManager;
/** /**
* BaseController constructor. * BaseController constructor.
* *
@ -64,6 +72,7 @@ abstract class BaseController
$this->container = $container; $this->container = $container;
$session = SessionManager::getSession(); $session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class); $this->sessionSegment = $session->getSegment(self::class);
$this->localeManager = $this->container->get('locale');
if ($this->config->stream) { if ($this->config->stream) {
$this->defaultFormat = 'best'; $this->defaultFormat = 'best';

View file

@ -166,7 +166,7 @@ class DownloadController extends BaseController
$response = $response->withHeader('Content-Type', 'video/' . $this->video->ext); $response = $response->withHeader('Content-Type', 'video/' . $this->video->ext);
$body = new Stream($this->video->getM3uStream()); $body = new Stream($this->video->getM3uStream());
} else { } else {
$headers = (array) $this->video->http_headers; $headers = [];
$range = $request->getHeader('Range'); $range = $request->getHeader('Range');
if (!empty($range)) { if (!empty($range)) {
@ -212,7 +212,7 @@ class DownloadController extends BaseController
private function getRemuxStream(Request $request, Response $response) private function getRemuxStream(Request $request, Response $response)
{ {
if (!$this->config->remux) { if (!$this->config->remux) {
throw new Exception(_('You need to enable remux mode to merge two formats.')); throw new Exception($this->localeManager->t('You need to enable remux mode to merge two formats.'));
} }
$stream = $this->video->getRemuxStream(); $stream = $this->video->getRemuxStream();
$response = $response->withHeader('Content-Type', 'video/x-matroska'); $response = $response->withHeader('Content-Type', 'video/x-matroska');
@ -222,7 +222,7 @@ class DownloadController extends BaseController
return $response->withHeader( return $response->withHeader(
'Content-Disposition', 'Content-Disposition',
'attachment; filename="' . $this->video->getFileNameWithExtension('mkv') 'attachment; filename="' . $this->video->getFileNameWithExtension('mkv') . '"'
); );
} }
@ -252,7 +252,7 @@ class DownloadController extends BaseController
return $this->getStream($request, $response); return $this->getStream($request, $response);
} else { } else {
if (empty($videoUrls[0])) { if (empty($videoUrls[0])) {
throw new Exception(_("Can't find URL of video.")); throw new Exception($this->localeManager->t("Can't find URL of video."));
} }
return $response->withRedirect($videoUrls[0]); return $response->withRedirect($videoUrls[0]);

View file

@ -8,14 +8,16 @@ namespace Alltube\Controller;
use Alltube\Exception\PasswordException; use Alltube\Exception\PasswordException;
use Alltube\Locale; use Alltube\Locale;
use Alltube\LocaleManager;
use Alltube\Video; use Alltube\Video;
use Throwable;
use Exception; use Exception;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Slim\Container; use Slim\Container;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
use Slim\Views\Smarty; use Slim\Views\Smarty;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
/** /**
* Main controller. * Main controller.
@ -29,13 +31,6 @@ class FrontController extends BaseController
*/ */
private $view; private $view;
/**
* LocaleManager instance.
*
* @var LocaleManager
*/
private $localeManager;
/** /**
* BaseController constructor. * BaseController constructor.
* *
@ -45,7 +40,6 @@ class FrontController extends BaseController
{ {
parent::__construct($container); parent::__construct($container);
$this->localeManager = $this->container->get('locale');
$this->view = $this->container->get('view'); $this->view = $this->container->get('view');
} }
@ -66,7 +60,9 @@ class FrontController extends BaseController
[ [
'config' => $this->config, 'config' => $this->config,
'class' => 'index', 'class' => 'index',
'description' => _('Easily download videos from Youtube, Dailymotion, Vimeo and other websites.'), 'description' => $this->localeManager->t(
'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(), 'supportedLocales' => $this->localeManager->getSupportedLocales(),
@ -110,8 +106,8 @@ class FrontController extends BaseController
'config' => $this->config, 'config' => $this->config,
'extractors' => Video::getExtractors(), 'extractors' => Video::getExtractors(),
'class' => 'extractors', 'class' => 'extractors',
'title' => _('Supported websites'), 'title' => $this->localeManager->t('Supported websites'),
'description' => _('List of all supported websites from which Alltube Download ' . 'description' => $this->localeManager->t('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(), 'locale' => $this->localeManager->getLocale(),
@ -137,8 +133,10 @@ class FrontController extends BaseController
[ [
'config' => $this->config, 'config' => $this->config,
'class' => 'password', 'class' => 'password',
'title' => _('Password prompt'), 'title' => $this->localeManager->t('Password prompt'),
'description' => _('You need a password in order to download this video with Alltube Download'), 'description' => $this->localeManager->t(
'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(), 'locale' => $this->localeManager->getLocale(),
] ]
@ -168,12 +166,20 @@ class FrontController extends BaseController
} else { } else {
$template = 'info.tpl'; $template = 'info.tpl';
} }
$title = _('Video download'); $title = $this->localeManager->t('Video download');
$description = _('Download video from ') . $this->video->extractor_key; $description = $this->localeManager->t(
'Download video from @extractor',
['@extractor' => $this->video->extractor_key]
);
if (isset($this->video->title)) { if (isset($this->video->title)) {
$title = $this->video->title; $title = $this->video->title;
$description = _('Download') . ' "' . $this->video->title . '" ' . $description = $this->localeManager->t(
_('from') . ' ' . $this->video->extractor_key; 'Download @title from @extractor',
[
'@title' => $this->video->title,
'@extractor' => $this->video->extractor_key
]
);
} }
$this->view->render( $this->view->render(
$response, $response,
@ -233,6 +239,10 @@ class FrontController extends BaseController
*/ */
public function error(Request $request, Response $response, Exception $exception) public function error(Request $request, Response $response, Exception $exception)
{ {
if ($this->config->debug) {
$handler = new ExceptionHandler();
$handler->handle($exception);
} else {
$this->view->render( $this->view->render(
$response, $response,
'error.tpl', 'error.tpl',
@ -240,11 +250,43 @@ class FrontController extends BaseController
'config' => $this->config, 'config' => $this->config,
'errors' => $exception->getMessage(), 'errors' => $exception->getMessage(),
'class' => 'video', 'class' => 'video',
'title' => _('Error'), 'title' => $this->localeManager->t('Error'),
'canonical' => $this->getCanonicalUrl($request), 'canonical' => $this->getCanonicalUrl($request),
'locale' => $this->localeManager->getLocale(), 'locale' => $this->localeManager->getLocale(),
] ]
); );
}
return $response->withStatus(500);
}
/**
* Display an error page for fatal errors.
*
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
* @param Throwable $error Error to display
*
* @return Response HTTP response
*/
public function fatalError(Request $request, Response $response, Throwable $error)
{
if ($this->config->debug) {
$handler = new ExceptionHandler();
$handler->handle(new FatalThrowableError($error));
} else {
$this->view->render(
$response,
'error.tpl',
[
'config' => $this->config,
'class' => 'video',
'title' => $this->localeManager->t('Error'),
'canonical' => $this->getCanonicalUrl($request),
'locale' => $this->localeManager->getLocale(),
]
);
}
return $response->withStatus(500); return $response->withStatus(500);
} }

View file

@ -9,15 +9,15 @@ msgstr ""
#: templates/error.tpl:6 #: templates/error.tpl:6
msgid "Please check the URL of your video." msgid "Please check the URL of your video."
msgstr "Porfavor, revise la URL de su video." msgstr "Por favor, revise la URL de su vídeo."
#: templates/playlist.tpl:5 #: templates/playlist.tpl:5
msgid "Videos extracted from" msgid "Videos extracted from"
msgstr "Videos extraidos desde" msgstr "Vídeos extraídos desde"
#: templates/playlist.tpl:7 #: templates/playlist.tpl:7
msgid ":" msgid ":"
msgstr "" msgstr ":"
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83 #: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83
#: templates/video.tpl:86 templates/index.tpl:18 #: templates/video.tpl:86 templates/index.tpl:18
@ -55,23 +55,23 @@ msgstr "Formatos disponibles:"
#: templates/video.tpl:29 #: templates/video.tpl:29
msgid "Generic formats" msgid "Generic formats"
msgstr "Formatos genericos" msgstr "Formatos genéricos"
#: templates/video.tpl:32 #: templates/video.tpl:32
msgid "Best" msgid "Best"
msgstr "Mejor" msgstr "El mejor"
#: templates/video.tpl:37 #: templates/video.tpl:37
msgid "Remux best video with best audio" msgid "Remux best video with best audio"
msgstr "" msgstr "Usar la mejor calidad de vídeo y audio"
#: templates/video.tpl:41 #: templates/video.tpl:41
msgid "Worst" msgid "Worst"
msgstr "Peor" msgstr "El peor"
#: templates/video.tpl:44 #: templates/video.tpl:44
msgid "Detailed formats" msgid "Detailed formats"
msgstr "" msgstr "Formatos detallados"
#: templates/inc/footer.tpl:4 #: templates/inc/footer.tpl:4
msgid "Code by" msgid "Code by"
@ -107,11 +107,11 @@ msgstr "Compartir en Facebook"
#: templates/index.tpl:8 #: templates/index.tpl:8
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)" msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
msgstr "Copia aqui la URL de tu video (Youtube, Dailymotion, etc.)" msgstr "Copia aquí la URL de tu vídeo (Youtube, Dailymotion, etc.)"
#: templates/index.tpl:23 #: templates/index.tpl:23
msgid "Audio only (MP3)" msgid "Audio only (MP3)"
msgstr "Solo audio (MP3)" msgstr "Sólo audio (MP3)"
#: templates/index.tpl:28 #: templates/index.tpl:28
msgid "See all supported websites" msgid "See all supported websites"
@ -123,7 +123,7 @@ msgstr "Arrastra esto a tu barra de marcadores"
#: templates/index.tpl:31 #: templates/index.tpl:31
msgid "Bookmarklet" msgid "Bookmarklet"
msgstr "" msgstr "Marcador"
#: templates/inc/header.tpl:4 #: templates/inc/header.tpl:4
msgid "Switch language" msgid "Switch language"

View file

@ -1,34 +1,61 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: AllTube Download\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: POEditor.com\n" "X-Generator: Poedit 2.2.4\n"
"Project-Id-Version: AllTube Download\n"
"Language: fr\n"
#: templates/error.tpl:6 #: templates/playlist.tpl:13
msgid "Please check the URL of your video." msgid "Videos extracted from @title:"
msgstr "Veuillez vérifier l'URL de votre vidéo" msgstr "Vidéos extraites depuis @title&nbsp;:"
#: templates/playlist.tpl:5 #: templates/playlist.tpl:38 templates/password.tpl:11 templates/index.tpl:19
msgid "Videos extracted from" #: templates/info.tpl:98
msgstr "Vidéos extraites depuis"
#: templates/playlist.tpl:7
msgid ":"
msgstr " :"
#: templates/playlist.tpl:26 templates/video.tpl:96 templates/video.tpl:99
#: templates/password.tpl:10 templates/index.tpl:19
#: controllers/FrontController.php:315
msgid "Download" msgid "Download"
msgstr "Télécharger" msgstr "Télécharger"
#: templates/playlist.tpl:27 #: templates/playlist.tpl:39
msgid "More options" msgid "More options"
msgstr "Plus d'options" msgstr "Plus d'options"
#: templates/inc/header.tpl:4
msgid "Switch language"
msgstr "Changer de langue"
#: templates/inc/header.tpl:8
msgid "Set language"
msgstr "Choisir la langue"
#: templates/inc/footer.tpl:8
msgid "Code by @dev"
msgstr "Développé par @dev"
#: templates/inc/footer.tpl:16
msgid "Design by @designer"
msgstr "Designé par @designer"
#: templates/inc/footer.tpl:21
msgid "Get the code"
msgstr "Obtenir le code"
#: templates/inc/footer.tpl:29
msgid "Based on @youtubedl"
msgstr "Basé sur @youtubedl"
#: templates/inc/footer.tpl:33
msgid "Donate using Liberapay"
msgstr "Faire un don avec Liberapay"
#: templates/inc/footer.tpl:35
msgid "Donate"
msgstr "Faire un don"
#: templates/password.tpl:5 #: templates/password.tpl:5
msgid "This video is protected" msgid "This video is protected"
msgstr "Cette vidéo est protégée" msgstr "Cette vidéo est protégée"
@ -41,62 +68,6 @@ msgstr "L'accès à cette vidéo nécessite un mot de passe."
msgid "Video password" msgid "Video password"
msgstr "Mot de passe de la vidéo" msgstr "Mot de passe de la vidéo"
#: templates/extractors.tpl:4 controllers/FrontController.php:167
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:24
msgid "Available formats:"
msgstr "Formats disponibles :"
#: templates/video.tpl:29
msgid "Generic formats"
msgstr "Formats génériques"
#: templates/video.tpl:32
msgid "Best"
msgstr "Meilleure qualité"
#: templates/video.tpl:37
msgid "Remux best video with best audio"
msgstr "Combiner la meilleure vidéo avec le meilleur audio"
#: templates/video.tpl:41
msgid "Worst"
msgstr "Pire qualité"
#: templates/video.tpl:44
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:10
msgid "Get the code"
msgstr "Obtenir le code"
#: templates/inc/footer.tpl:12
msgid "Based on"
msgstr "Basé sur"
#: templates/inc/header.tpl:21
msgid "Share on Twitter"
msgstr "Partager sur Twitter"
#: templates/inc/header.tpl:23
msgid "Share on Facebook"
msgstr "Partager sur Facebook"
#: templates/index.tpl:8 #: templates/index.tpl:8
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)" msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
msgstr "Copiez ici l'URL de votre vidéo (Youtube, Dailymotion, etc.)" msgstr "Copiez ici l'URL de votre vidéo (Youtube, Dailymotion, etc.)"
@ -105,6 +76,14 @@ msgstr "Copiez ici l'URL de votre vidéo (Youtube, Dailymotion, etc.)"
msgid "Audio only (MP3)" msgid "Audio only (MP3)"
msgstr "Audio uniquement (MP3)" msgstr "Audio uniquement (MP3)"
#: templates/index.tpl:28
msgid "From"
msgstr "À partir de"
#: templates/index.tpl:29
msgid "to"
msgstr "jusqu'à"
#: templates/index.tpl:36 #: templates/index.tpl:36
msgid "See all supported websites" msgid "See all supported websites"
msgstr "Voir tous les sites supportés" msgstr "Voir tous les sites supportés"
@ -117,121 +96,183 @@ msgstr "Glissez ce lien dans votre barre de favoris :"
msgid "Bookmarklet" msgid "Bookmarklet"
msgstr "Bookmarklet" msgstr "Bookmarklet"
#: templates/inc/header.tpl:4 #: templates/info.tpl:13
msgid "Switch language" msgid "You are going to download @title."
msgstr "Changer de langue" msgstr "Vous allez télécharger @title."
#: templates/info.tpl:31
msgid "Available formats:"
msgstr "Formats disponibles&nbsp;:"
#: templates/info.tpl:33
msgid "Generic formats"
msgstr "Formats génériques"
#: templates/info.tpl:38
msgid "Detailed formats"
msgstr "Formats détaillés"
#: templates/info.tpl:80
msgid "Stream the video through the server"
msgstr "Transférer la vidéo à travers le serveur"
#: templates/info.tpl:85
msgid "Convert into a custom format:"
msgstr "Convertir dans un format personnalisé :"
#: templates/info.tpl:86
msgid "Custom format"
msgstr "Format personnalisé"
#: templates/info.tpl:86
msgid "Format to convert to"
msgstr "Format vers lequel convertir"
#: templates/info.tpl:91
msgid "with"
msgstr "avec de l'audio à"
#: templates/info.tpl:92
msgid "Bit rate"
msgstr "Débit binaire"
#: templates/info.tpl:93
msgid "Custom bitrate"
msgstr "Débit binaire personnalisé"
#: templates/info.tpl:95
msgid "kbit/s audio"
msgstr "kbit/s"
#: templates/error.tpl:5 #: templates/error.tpl:5
msgid "An error occurred" msgid "An error occurred"
msgstr "Une erreur est survenue" msgstr "Une erreur est survenue"
#: templates/video.tpl:85 #: templates/error.tpl:6
msgid "Convert into a custom format:" msgid "Please check the URL of your video."
msgstr "Convertir dans un format personnalisé :" msgstr "Veuillez vérifier l'URL de votre vidéo."
#: templates/video.tpl:93 #: templates/extractors.tpl:4 controllers/FrontController.php:109
msgid "kbit/s audio" msgid "Supported websites"
msgstr "kbit/s" msgstr "Sites web supportés"
#: templates/video.tpl:91 #: classes/Config.php:158
msgid "with" msgid "Best"
msgstr "avec de l'audio à" msgstr "Meilleure qualité"
#: classes/VideoDownload.php:117 #: classes/Config.php:159
msgid "Remux best video with best audio"
msgstr "Combiner la meilleure vidéo avec le meilleur audio"
#: classes/Config.php:160
msgid "Worst"
msgstr "Pire qualité"
#: classes/Video.php:159
msgid "Wrong password" msgid "Wrong password"
msgstr "Mauvais mot de passe" msgstr "Mauvais mot de passe"
#: classes/VideoDownload.php:364 classes/VideoDownload.php:526 #: classes/Video.php:250
msgid "Conversion of M3U8 files is not supported."
msgstr "La conversion des fichiers M3U8 n'est pas possible."
#: classes/VideoDownload.php:375 classes/VideoDownload.php:412
#: classes/VideoDownload.php:445 classes/VideoDownload.php:478
#: classes/VideoDownload.php:534
msgid "Could not open popen stream."
msgstr "Impossible d'ouvrir le flux popen."
#: classes/VideoDownload.php:502
msgid "Could not open fopen stream."
msgstr "Impossible d'ouvrir le flux fopen."
#: controllers/FrontController.php:124
msgid "Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
msgstr "Téléchargez facilement des vidéos depuis Youtube, Dailymotion, Vimeo et d'autres sites web."
#: controllers/FrontController.php:168
msgid "List of all supported websites from which Alltube Download can extract video or audio files"
msgstr "Liste de tous les sites web depuis lesquels Alltube Download peut extraire des fichiers vidéo ou audio"
#: controllers/FrontController.php:193
msgid "Password prompt"
msgstr "Demande de mot de passe"
#: controllers/FrontController.php:194
msgid "You need a password in order to download this video with Alltube Download"
msgstr "Vous avez besoin d'un mot de passe pour télécharger cette vidéo avec Alltube Download"
#: controllers/FrontController.php:311
msgid "Video download"
msgstr "Téléchargement d'une vidéo"
#: controllers/FrontController.php:312
msgid "Download video from "
msgstr "Téléchargement d'une vidéo depuis "
#: controllers/FrontController.php:315
msgid "from"
msgstr "depuis"
#: controllers/FrontController.php:378
msgid "Error"
msgstr "Erreur"
#: controllers/FrontController.php:450
msgid "You need to enable remux mode to merge two formats."
msgstr "Vous devez activer le mode remux pour fusionner deux formats."
#: controllers/FrontController.php:525
msgid "Can't find URL of video."
msgstr "Impossible de trouver l'URL de la vidéo."
#: classes/VideoDownload.php:289 classes/VideoDownload.php:394
msgid "Can't find avconv or ffmpeg at "
msgstr "Impossible de trouver avconv ou ffmpeg à "
#: templates/inc/footer.tpl:14
msgid "Donate"
msgstr "Faire un don"
#: classes/VideoDownload.php:158
msgid "youtube-dl returned an empty URL." msgid "youtube-dl returned an empty URL."
msgstr "youtube-dl a retourné une URL vide." msgstr "youtube-dl a retourné une URL vide."
#: classes/VideoDownload.php:359 #: classes/Video.php:361 classes/Video.php:465
msgid "Can't find avconv or ffmpeg at @path."
msgstr "Impossible de trouver avconv ou ffmpeg à @path."
#: classes/Video.php:377
msgid "Invalid start time: @from."
msgstr "Horodatage de début non-valide&nbsp;: @from."
#: classes/Video.php:384
msgid "Invalid end time: @to."
msgstr "Horodatage de fin non-valide&nbsp;: @to."
#: classes/Video.php:430
msgid "Conversion of playlists is not supported." msgid "Conversion of playlists is not supported."
msgstr "Impossible de convertir une playlist." msgstr "Impossible de convertir une playlist."
#: classes/VideoDownload.php:366 #: classes/Video.php:435 classes/Video.php:578
msgid "Conversion of M3U8 files is not supported."
msgstr "La conversion des fichiers M3U8 n'est pas possible."
#: classes/Video.php:437
msgid "Conversion of DASH segments is not supported." msgid "Conversion of DASH segments is not supported."
msgstr "Impossible de convertir des segments DASH." msgstr "Impossible de convertir des segments DASH."
#: templates/inc/footer.tpl:14 #: classes/Video.php:446 classes/Video.php:488 classes/Video.php:525
msgid "Donate using Liberapay" #: classes/Video.php:558 classes/Video.php:586
msgstr "Faire un don avec Liberapay" msgid "Could not open popen stream."
msgstr "Impossible d'ouvrir le flux popen."
#: classes/VideoDownload.php:302 #: classes/Video.php:506
msgid "Invalid start time: " msgid "This video does not have two URLs."
msgstr "Horodatage de début non-valide :␣" msgstr "Cette vidéo n'a pas deux URL."
#: classes/VideoDownload.php:309 #: controllers/DownloadController.php:215
msgid "Invalid end time: " msgid "You need to enable remux mode to merge two formats."
msgstr "Horodatage de fin non-valide :␣" msgstr "Vous devez activer le mode remux pour fusionner deux formats."
#: templates/index.tpl:28 #: controllers/DownloadController.php:255
msgid "From" msgid "Can't find URL of video."
msgstr "À partir de" msgstr "Impossible de trouver l'URL de la vidéo."
#: templates/index.tpl:29 #: controllers/FrontController.php:64
msgid "to" msgid ""
msgstr "jusqu'à" "Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
msgstr ""
"Téléchargez facilement des vidéos depuis Youtube, Dailymotion, Vimeo et "
"d'autres sites web."
#: controllers/FrontController.php:110
msgid ""
"List of all supported websites from which Alltube Download can extract video "
"or audio files"
msgstr ""
"Liste de tous les sites web depuis lesquels Alltube Download peut extraire "
"des fichiers vidéo ou audio"
#: controllers/FrontController.php:136
msgid "Password prompt"
msgstr "Demande de mot de passe"
#: controllers/FrontController.php:138
msgid ""
"You need a password in order to download this video with Alltube Download"
msgstr ""
"Vous avez besoin d'un mot de passe pour télécharger cette vidéo avec Alltube "
"Download"
#: controllers/FrontController.php:169
msgid "Video download"
msgstr "Téléchargement d'une vidéo"
#: controllers/FrontController.php:171
msgid "Download video from @extractor"
msgstr "Téléchargement d'une vidéo depuis @extractor"
#: controllers/FrontController.php:177
msgid "Download @title from @extractor"
msgstr "Téléchargement de @title depuis @extractor"
#: controllers/FrontController.php:253 controllers/FrontController.php:284
msgid "Error"
msgstr "Erreur"
#~ msgid ":"
#~ msgstr " :"
#~ msgid "Based on"
#~ msgstr "Basé sur"
#~ msgid "Share on Twitter"
#~ msgstr "Partager sur Twitter"
#~ msgid "Share on Facebook"
#~ msgstr "Partager sur Facebook"
#~ msgid "Could not open fopen stream."
#~ msgstr "Impossible d'ouvrir le flux fopen."
#~ msgid "from"
#~ msgstr "depuis"

View file

@ -1,110 +1,49 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n" msgstr "Content-Type: text/plain; charset=UTF-8\n"
#: templates/inc/footer.tpl:4 #: templates/playlist.tpl:13
msgid "Code by" msgid "Videos extracted from @title:"
msgstr "" msgstr ""
#: templates/inc/footer.tpl:6 #: templates/playlist.tpl:38 templates/password.tpl:11 templates/index.tpl:19
msgid "Design by" #: templates/info.tpl:98
msgid "Download"
msgstr "" msgstr ""
#: templates/inc/footer.tpl:10 #: templates/playlist.tpl:39
msgid "Get the code" msgid "More options"
msgstr ""
#: templates/inc/footer.tpl:12
msgid "Based on"
msgstr ""
#: templates/inc/footer.tpl:14
msgid "Donate using Liberapay"
msgstr ""
#: templates/inc/footer.tpl:14
msgid "Donate"
msgstr "" msgstr ""
#: templates/inc/header.tpl:4 #: templates/inc/header.tpl:4
msgid "Switch language" msgid "Switch language"
msgstr "" msgstr ""
#: templates/inc/header.tpl:21 #: templates/inc/header.tpl:8
msgid "Share on Twitter" msgid "Set language"
msgstr "" msgstr ""
#: templates/inc/header.tpl:23 #: templates/inc/footer.tpl:8
msgid "Share on Facebook" msgid "Code by @dev"
msgstr "" msgstr ""
#: templates/playlist.tpl:5 #: templates/inc/footer.tpl:16
msgid "Videos extracted from" msgid "Design by @designer"
msgstr "" msgstr ""
#: templates/playlist.tpl:7 #: templates/inc/footer.tpl:21
msgid ":" msgid "Get the code"
msgstr "" msgstr ""
#: templates/playlist.tpl:26 templates/video.tpl:96 templates/video.tpl:99 #: templates/inc/footer.tpl:29
#: templates/password.tpl:10 templates/index.tpl:19 msgid "Based on @youtubedl"
#: controllers/FrontController.php:315
msgid "Download"
msgstr "" msgstr ""
#: templates/playlist.tpl:27 #: templates/inc/footer.tpl:33
msgid "More options" msgid "Donate using Liberapay"
msgstr "" msgstr ""
#: templates/extractors.tpl:4 controllers/FrontController.php:167 #: templates/inc/footer.tpl:35
msgid "Supported websites" msgid "Donate"
msgstr ""
#: templates/video.tpl:6
msgid "You are going to download"
msgstr ""
#: templates/video.tpl:24
msgid "Available formats:"
msgstr ""
#: templates/video.tpl:29
msgid "Generic formats"
msgstr ""
#: templates/video.tpl:32
msgid "Best"
msgstr ""
#: templates/video.tpl:37
msgid "Remux best video with best audio"
msgstr ""
#: templates/video.tpl:41
msgid "Worst"
msgstr ""
#: templates/video.tpl:44
msgid "Detailed formats"
msgstr ""
#: templates/video.tpl:85
msgid "Convert into a custom format:"
msgstr ""
#: templates/video.tpl:91
msgid "with"
msgstr ""
#: templates/video.tpl:93
msgid "kbit/s audio"
msgstr ""
#: templates/error.tpl:5
msgid "An error occurred"
msgstr ""
#: templates/error.tpl:6
msgid "Please check the URL of your video."
msgstr "" msgstr ""
#: templates/password.tpl:5 #: templates/password.tpl:5
@ -147,88 +86,159 @@ msgstr ""
msgid "Bookmarklet" msgid "Bookmarklet"
msgstr "" msgstr ""
#: classes/VideoDownload.php:117 #: templates/info.tpl:13
msgid "You are going to download @title."
msgstr ""
#: templates/info.tpl:31
msgid "Available formats:"
msgstr ""
#: templates/info.tpl:33
msgid "Generic formats"
msgstr ""
#: templates/info.tpl:38
msgid "Detailed formats"
msgstr ""
#: templates/info.tpl:80
msgid "Stream the video through the server"
msgstr ""
#: templates/info.tpl:85
msgid "Convert into a custom format:"
msgstr ""
#: templates/info.tpl:86
msgid "Custom format"
msgstr ""
#: templates/info.tpl:86
msgid "Format to convert to"
msgstr ""
#: templates/info.tpl:91
msgid "with"
msgstr ""
#: templates/info.tpl:92
msgid "Bit rate"
msgstr ""
#: templates/info.tpl:93
msgid "Custom bitrate"
msgstr ""
#: templates/info.tpl:95
msgid "kbit/s audio"
msgstr ""
#: templates/error.tpl:5
msgid "An error occurred"
msgstr ""
#: templates/error.tpl:6
msgid "Please check the URL of your video."
msgstr ""
#: templates/extractors.tpl:4 controllers/FrontController.php:109
msgid "Supported websites"
msgstr ""
#: classes/Config.php:158
msgid "Best"
msgstr ""
#: classes/Config.php:159
msgid "Remux best video with best audio"
msgstr ""
#: classes/Config.php:160
msgid "Worst"
msgstr ""
#: classes/Video.php:159
msgid "Wrong password" msgid "Wrong password"
msgstr "" msgstr ""
#: classes/VideoDownload.php:158 #: classes/Video.php:250
msgid "youtube-dl returned an empty URL." msgid "youtube-dl returned an empty URL."
msgstr "" msgstr ""
#: classes/VideoDownload.php:289 classes/VideoDownload.php:394 #: classes/Video.php:361 classes/Video.php:465
msgid "Can't find avconv or ffmpeg at " msgid "Can't find avconv or ffmpeg at @path."
msgstr "" msgstr ""
#: classes/VideoDownload.php:302 #: classes/Video.php:377
msgid "Invalid start time: " msgid "Invalid start time: @from."
msgstr "" msgstr ""
#: classes/VideoDownload.php:309 #: classes/Video.php:384
msgid "Invalid end time: " msgid "Invalid end time: @to."
msgstr "" msgstr ""
#: classes/VideoDownload.php:359 #: classes/Video.php:430
msgid "Conversion of playlists is not supported." msgid "Conversion of playlists is not supported."
msgstr "" msgstr ""
#: classes/VideoDownload.php:364 classes/VideoDownload.php:526 #: classes/Video.php:435 classes/Video.php:578
msgid "Conversion of M3U8 files is not supported." msgid "Conversion of M3U8 files is not supported."
msgstr "" msgstr ""
#: classes/VideoDownload.php:366 #: classes/Video.php:437
msgid "Conversion of DASH segments is not supported." msgid "Conversion of DASH segments is not supported."
msgstr "" msgstr ""
#: classes/VideoDownload.php:375 classes/VideoDownload.php:412 #: classes/Video.php:446 classes/Video.php:488 classes/Video.php:525
#: classes/VideoDownload.php:445 classes/VideoDownload.php:478 #: classes/Video.php:558 classes/Video.php:586
#: classes/VideoDownload.php:534
msgid "Could not open popen stream." msgid "Could not open popen stream."
msgstr "" msgstr ""
#: classes/VideoDownload.php:502 #: classes/Video.php:506
msgid "Could not open fopen stream." msgid "This video does not have two URLs."
msgstr "" msgstr ""
#: controllers/FrontController.php:124 #: controllers/DownloadController.php:215
msgid "You need to enable remux mode to merge two formats."
msgstr ""
#: controllers/DownloadController.php:255
msgid "Can't find URL of video."
msgstr ""
#: controllers/FrontController.php:64
msgid "" msgid ""
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites." "Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
msgstr "" msgstr ""
#: controllers/FrontController.php:168 #: controllers/FrontController.php:110
msgid "" msgid ""
"List of all supported websites from which Alltube Download can extract video " "List of all supported websites from which Alltube Download can extract video "
"or audio files" "or audio files"
msgstr "" msgstr ""
#: controllers/FrontController.php:193 #: controllers/FrontController.php:136
msgid "Password prompt" msgid "Password prompt"
msgstr "" msgstr ""
#: controllers/FrontController.php:194 #: controllers/FrontController.php:138
msgid "" msgid ""
"You need a password in order to download this video with Alltube Download" "You need a password in order to download this video with Alltube Download"
msgstr "" msgstr ""
#: controllers/FrontController.php:311 #: controllers/FrontController.php:169
msgid "Video download" msgid "Video download"
msgstr "" msgstr ""
#: controllers/FrontController.php:312 #: controllers/FrontController.php:171
msgid "Download video from " msgid "Download video from @extractor"
msgstr "" msgstr ""
#: controllers/FrontController.php:315 #: controllers/FrontController.php:177
msgid "from" msgid "Download @title from @extractor"
msgstr "" msgstr ""
#: controllers/FrontController.php:378 #: controllers/FrontController.php:253 controllers/FrontController.php:284
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: controllers/FrontController.php:450
msgid "You need to enable remux mode to merge two formats."
msgstr ""
#: controllers/FrontController.php:525
msgid "Can't find URL of video."
msgstr ""

View file

@ -10,6 +10,7 @@ use Alltube\LocaleMiddleware;
use Alltube\UglyRouter; use Alltube\UglyRouter;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\App; use Slim\App;
use Symfony\Component\Debug\Debug;
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) { if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI'])); header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
@ -20,26 +21,44 @@ if (is_file(__DIR__ . '/config/config.yml')) {
Config::setFile(__DIR__ . '/config/config.yml'); Config::setFile(__DIR__ . '/config/config.yml');
} }
// Create app.
$app = new App(); $app = new App();
$container = $app->getContainer(); $container = $app->getContainer();
// Load config.
$config = Config::getInstance(); $config = Config::getInstance();
if ($config->uglyUrls) { if ($config->uglyUrls) {
$container['router'] = new UglyRouter(); $container['router'] = new UglyRouter();
} }
$container['view'] = ViewFactory::create($container); if ($config->debug) {
/*
We want to enable this as soon as possible,
in order to catch errors that are thrown
before the Slim error handler is ready.
*/
Debug::enable();
}
// Locales.
if (!class_exists('Locale')) { if (!class_exists('Locale')) {
die('You need to install the intl extension for PHP.'); die('You need to install the intl extension for PHP.');
} }
$container['locale'] = new LocaleManager(); $container['locale'] = LocaleManager::getInstance();
$app->add(new LocaleMiddleware($container)); $app->add(new LocaleMiddleware($container));
// Smarty.
$container['view'] = ViewFactory::create($container);
// Controllers.
$frontController = new FrontController($container); $frontController = new FrontController($container);
$jsonController = new JsonController($container); $jsonController = new JsonController($container);
$downloadController = new DownloadController($container); $downloadController = new DownloadController($container);
// Error handling.
$container['errorHandler'] = [$frontController, 'error']; $container['errorHandler'] = [$frontController, 'error'];
$container['phpErrorHandler'] = [$frontController, 'fatalError'];
// Routes.
$app->get( $app->get(
'/', '/',
[$frontController, 'index'] [$frontController, 'index']
@ -59,7 +78,7 @@ $app->any('/video', [$frontController, 'info']);
$app->any( $app->any(
'/watch', '/watch',
[$frontController, 'video'] [$frontController, 'info']
); );
$app->any( $app->any(

View file

@ -1,7 +1,7 @@
{ {
"name": "alltube", "name": "alltube",
"description": "HTML GUI for youtube-dl", "description": "HTML GUI for youtube-dl",
"version": "2.0.5", "version": "2.1.0",
"author": "Pierre Rudloff", "author": "Pierre Rudloff",
"bugs": "https://github.com/Rudloff/alltube/issues", "bugs": "https://github.com/Rudloff/alltube/issues",
"dependencies": { "dependencies": {

View file

@ -5,27 +5,27 @@
"display": "standalone", "display": "standalone",
"icons": [ "icons": [
{ {
"src": "img/favicon.png", "src": "../img/favicon.png",
"sizes": "32x32", "sizes": "32x32",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "img/logo_60.png", "src": "../img/logo_60.png",
"sizes": "60x60", "sizes": "60x60",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "img/logo_90.png", "src": "../img/logo_90.png",
"sizes": "90x60", "sizes": "90x60",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "img/logo_app.png", "src": "../img/logo_app.png",
"sizes": "243x243", "sizes": "243x243",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "img/logo_250.png", "src": "../img/logo_250.png",
"sizes": "250x250", "sizes": "250x250",
"type": "image/png" "type": "image/png"
} }

View file

@ -1,17 +1,39 @@
</div> </div>
<footer class="small-font"> <footer class="small-font">
<div class="footer_wrapper"> <div class="footer_wrapper">
{t}Code by{/t} <a rel="author" target="blank" {$dev="<a rel='author' target='blank'
href="http://rudloff.pro/">Pierre Rudloff</a> href='http://rudloff.pro/'>
&middot; {t}Design by{/t} Pierre Rudloff
<a rel="author" target="blank" </a>"}
href="http://olivierhaquette.fr">Olivier Haquette</a> {t params=['@dev'=>$dev]}Code by @dev{/t}
&middot; &middot;
<a rel="noopener" target="_blank" href="https://github.com/Rudloff/alltube">{t}Get the code{/t}</a>
{$designer="<a rel='author' target='blank'
href='http://olivierhaquette.fr'>
Olivier Haquette
</a>"}
{t params=['@designer' => $designer]}Design by @designer{/t}
&middot; &middot;
{t}Based on{/t} <a href="http://rg3.github.io/youtube-dl/">youtube-dl</a>
<a rel="noopener" target="_blank" href="https://github.com/Rudloff/alltube">
{t}Get the code{/t}
</a>
&middot; &middot;
<a rel="noopener" target="_blank" title="{t}Donate using Liberapay{/t}" href="https://liberapay.com/Rudloff/donate">{t}Donate{/t}</a>
{$youtubedl="<a href='http://rg3.github.io/youtube-dl/'>
youtube-dl
</a>"}
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t}
&middot;
<a rel="noopener" target="_blank" title="{t}Donate using Liberapay{/t}"
href="https://liberapay.com/Rudloff/donate">
{t}Donate{/t}
</a>
</div> </div>
</footer> </footer>
</body> </body>

View file

@ -1,4 +1,3 @@
{locale path="../i18n" domain="Alltube"}
<!doctype html> <!doctype html>
<html {if isset($locale)}lang="{$locale->getBcp47()}"{/if}> <html {if isset($locale)}lang="{$locale->getBcp47()}"{/if}>
<head> <head>

View file

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

View file

@ -3,10 +3,12 @@
<div itemscope itemtype="http://schema.org/VideoObject"> <div itemscope itemtype="http://schema.org/VideoObject">
<main class="main"> <main class="main">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<p id="download_intro">{t}You are going to download{/t}<i itemprop="name"> {$title="<i itemprop='name'>
<a itemprop="url" id="video_link" <a itemprop='url' id='video_link'
href="{$video->webpage_url}"> href='{$video->webpage_url}'>
{$video->title}</a></i>. {$video->title}</a></i>"}
<p id="download_intro">
{t params=['@title' => $title]}You are going to download @title.{/t}
</p> </p>
{if isset($video->thumbnail)} {if isset($video->thumbnail)}
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt="" /> <img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt="" />
@ -20,11 +22,11 @@
<br/> <br/>
<form action="{path_for name="download"}"> <form action="{path_for name="download"}">
<input type="hidden" name="url" value="{$video->webpage_url}" /> <input type="hidden" name="url" value="{$video->webpage_url}" />
{if isset($video->formats) && count($video->formats) > 1}
<h3><label for="format">{t}Available formats:{/t}</label></h3>
{if $config->uglyUrls} {if $config->uglyUrls}
<input type="hidden" name="page" value="download" /> <input type="hidden" name="page" value="download" />
{/if} {/if}
{if isset($video->formats) && count($video->formats) > 1}
<h3><label for="format">{t}Available formats:{/t}</label></h3>
<select name="format" id="format" class="formats monospace"> <select name="format" id="format" class="formats monospace">
<optgroup label="{t}Generic formats{/t}"> <optgroup label="{t}Generic formats{/t}">
{foreach $config->genericFormats as $format => $name} {foreach $config->genericFormats as $format => $name}
@ -70,6 +72,7 @@
{/foreach} {/foreach}
</optgroup> </optgroup>
</select><br/><br/> </select><br/><br/>
{/if}
{if $config->stream} {if $config->stream}
<input type="checkbox" checked name="stream" id="stream"/> <input type="checkbox" checked name="stream" id="stream"/>
<label for="stream">{t}Stream the video through the server{/t}</label> <label for="stream">{t}Stream the video through the server{/t}</label>
@ -78,19 +81,18 @@
{if $config->convertAdvanced} {if $config->convertAdvanced}
<input type="checkbox" name="customConvert" id="customConvert"/> <input type="checkbox" name="customConvert" id="customConvert"/>
<label for="customConvert">{t}Convert into a custom format:{/t}</label> <label for="customConvert">{t}Convert into a custom format:{/t}</label>
<select title="Custom format" name="customFormat" aria-label="{t}Format to convert to{/t}"> <select title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}">
{foreach $config->convertAdvancedFormats as $format} {foreach $config->convertAdvancedFormats as $format}
<option>{$format}</option> <option>{$format}</option>
{/foreach} {/foreach}
</select> </select>
{t}with{/t} {t}with{/t}
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label> <label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
<input type="number" value="{$config->audioBitrate}" title="Custom bitrate" class="customBitrate" <input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}" class="customBitrate"
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit" /> name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit" />
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span> <span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
<br/><br/> <br/><br/>
{/if} {/if}
{/if}
<input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/> <input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/>
</form> </form>
</main> </main>

View file

@ -2,10 +2,17 @@
<div class="wrapper"> <div class="wrapper">
<main class="main"> <main class="main">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<p>{t}Videos extracted from{/t} {if isset($video->title)}<i>
<a href="{$video->webpage_url}"> {if isset($video->title)}
{$video->title}</a></i>{/if}{t}:{/t} {$title="<i>
</p> <a href='{$video->webpage_url}'>
{$video->title}</a>
</i>"}
<p>
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
</p>
{/if}
{if $config->stream} {if $config->stream}
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a> <a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
{/if} {/if}

View file

@ -45,4 +45,29 @@ abstract class BaseTest extends TestCase
{ {
Config::destroyInstance(); Config::destroyInstance();
} }
/**
* Check tests requirements.
* @return void
*/
protected function checkRequirements()
{
parent::checkRequirements();
$annotations = $this->getAnnotations();
$requires = [];
if (isset($annotations['class']['requires'])) {
$requires += $annotations['class']['requires'];
}
if (isset($annotations['method']['requires'])) {
$requires += $annotations['method']['requires'];
}
foreach ($requires as $require) {
if ($require == 'download' && getenv('CI')) {
$this->markTestSkipped('Do not run tests that download videos on CI.');
}
}
}
} }

View file

@ -56,8 +56,8 @@ abstract class ControllerTest extends BaseTest
$this->container = new Container(); $this->container = new Container();
$this->request = Request::createFromEnvironment(Environment::mock()); $this->request = Request::createFromEnvironment(Environment::mock());
$this->response = new Response(); $this->response = new Response();
$this->container['locale'] = LocaleManager::getInstance();
$this->container['view'] = ViewFactory::create($this->container, $this->request); $this->container['view'] = ViewFactory::create($this->container, $this->request);
$this->container['locale'] = new LocaleManager();
$frontController = new FrontController($this->container); $frontController = new FrontController($this->container);
$downloadController = new DownloadController($this->container); $downloadController = new DownloadController($this->container);

View file

@ -11,6 +11,7 @@ use Alltube\Video;
/** /**
* Unit tests for the ConvertedPlaylistArchiveStream class. * Unit tests for the ConvertedPlaylistArchiveStream class.
* @requires download
*/ */
class ConvertedPlaylistArchiveStreamTest extends StreamTest class ConvertedPlaylistArchiveStreamTest extends StreamTest
{ {

View file

@ -11,6 +11,7 @@ use Alltube\Controller\DownloadController;
/** /**
* Unit tests for the FrontController class. * Unit tests for the FrontController class.
* @requires download
*/ */
class DownloadControllerTest extends ControllerTest class DownloadControllerTest extends ControllerTest
{ {
@ -79,10 +80,6 @@ class DownloadControllerTest extends ControllerTest
*/ */
public function testDownloadWithM3uStream() public function testDownloadWithM3uStream()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Twitter returns a 429 error when the test is ran too many times.');
}
Config::setOptions(['stream' => true]); Config::setOptions(['stream' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
@ -153,9 +150,6 @@ class DownloadControllerTest extends ControllerTest
*/ */
public function testDownloadWithMissingPassword() public function testDownloadWithMissingPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']); $this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']);
} }

View file

@ -108,6 +108,7 @@ class FrontControllerTest extends ControllerTest
* Test the info() function. * Test the info() function.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfo() public function testInfo()
{ {
@ -118,6 +119,7 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with audio conversion. * Test the info() function with audio conversion.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithAudio() public function testInfoWithAudio()
{ {
@ -133,12 +135,10 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with audio conversion from a Vimeo video. * Test the info() function with audio conversion from a Vimeo video.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithVimeoAudio() public function testInfoWithVimeoAudio()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
Config::setOptions(['convert' => true]); Config::setOptions(['convert' => true]);
// So we can test the fallback to default format // So we can test the fallback to default format
@ -149,6 +149,7 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with audio enabled and an URL that doesn't need to be converted. * Test the info() function with audio enabled and an URL that doesn't need to be converted.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithUnconvertedAudio() public function testInfoWithUnconvertedAudio()
{ {
@ -167,12 +168,10 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with a password. * Test the info() function with a password.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithPassword() public function testInfoWithPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$result = $this->controller->info( $result = $this->controller->info(
$this->request->withQueryParams(['url' => 'http://vimeo.com/68375962']) $this->request->withQueryParams(['url' => 'http://vimeo.com/68375962'])
->withParsedBody(['password' => 'youtube-dl']), ->withParsedBody(['password' => 'youtube-dl']),
@ -185,12 +184,10 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with a missing password. * Test the info() function with a missing password.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithMissingPassword() public function testInfoWithMissingPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962']); $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962']);
$this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]);
} }
@ -199,6 +196,7 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with streams enabled. * Test the info() function with streams enabled.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithStream() public function testInfoWithStream()
{ {
@ -215,6 +213,7 @@ class FrontControllerTest extends ControllerTest
* Test the info() function with a playlist. * Test the info() function with a playlist.
* *
* @return void * @return void
* @requires download
*/ */
public function testInfoWithPlaylist() public function testInfoWithPlaylist()
{ {

View file

@ -27,6 +27,7 @@ class JsonControllerTest extends ControllerTest
* Test the json() function. * Test the json() function.
* *
* @return void * @return void
* @requires download
*/ */
public function testJson() public function testJson()
{ {
@ -37,6 +38,7 @@ class JsonControllerTest extends ControllerTest
* Test the json() function with an error. * Test the json() function with an error.
* *
* @return void * @return void
* @requires download
*/ */
public function testJsonWithError() public function testJsonWithError()
{ {

View file

@ -27,7 +27,7 @@ class LocaleManagerTest extends BaseTest
protected function setUp() protected function setUp()
{ {
$_SESSION[LocaleManager::class]['locale'] = 'foo_BAR'; $_SESSION[LocaleManager::class]['locale'] = 'foo_BAR';
$this->localeManager = new LocaleManager(); $this->localeManager = LocaleManager::getInstance();
} }
/** /**
@ -38,6 +38,7 @@ class LocaleManagerTest extends BaseTest
protected function tearDown() protected function tearDown()
{ {
$this->localeManager->unsetLocale(); $this->localeManager->unsetLocale();
LocaleManager::destroyInstance();
} }
/** /**
@ -93,6 +94,7 @@ class LocaleManagerTest extends BaseTest
*/ */
public function testEnv() public function testEnv()
{ {
putenv('LANG=foo_BAR');
$this->localeManager->setLocale(new Locale('foo_BAR')); $this->localeManager->setLocale(new Locale('foo_BAR'));
$this->assertEquals('foo_BAR', getenv('LANG')); $this->assertEquals('foo_BAR', getenv('LANG'));
} }

View file

@ -39,7 +39,7 @@ class LocaleMiddlewareTest extends BaseTest
protected function setUp() protected function setUp()
{ {
$this->container = new Container(); $this->container = new Container();
$this->container['locale'] = new LocaleManager(); $this->container['locale'] = LocaleManager::getInstance();
$this->middleware = new LocaleMiddleware($this->container); $this->middleware = new LocaleMiddleware($this->container);
} }
@ -51,6 +51,7 @@ class LocaleMiddlewareTest extends BaseTest
protected function tearDown() protected function tearDown()
{ {
$this->container['locale']->unsetLocale(); $this->container['locale']->unsetLocale();
LocaleManager::destroyInstance();
} }
/** /**

View file

@ -11,6 +11,7 @@ use Alltube\Video;
/** /**
* Unit tests for the PlaylistArchiveStream class. * Unit tests for the PlaylistArchiveStream class.
* @requires download
*/ */
class PlaylistArchiveStreamTest extends StreamTest class PlaylistArchiveStreamTest extends StreamTest
{ {

View file

@ -11,6 +11,7 @@ use Alltube\Video;
/** /**
* Unit tests for the Video class. * Unit tests for the Video class.
* @requires download
*/ */
class VideoTest extends BaseTest class VideoTest extends BaseTest
{ {
@ -58,10 +59,6 @@ class VideoTest extends BaseTest
*/ */
public function testgetUrlWithPassword() public function testgetUrlWithPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$video = new Video('http://vimeo.com/68375962', 'best', 'youtube-dl'); $video = new Video('http://vimeo.com/68375962', 'best', 'youtube-dl');
foreach ($video->getUrl() as $videoURL) { foreach ($video->getUrl() as $videoURL) {
$this->assertContains('vimeocdn.com', $videoURL); $this->assertContains('vimeocdn.com', $videoURL);
@ -76,10 +73,6 @@ class VideoTest extends BaseTest
*/ */
public function testgetUrlWithMissingPassword() public function testgetUrlWithMissingPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$video = new Video('http://vimeo.com/68375962'); $video = new Video('http://vimeo.com/68375962');
$video->getUrl(); $video->getUrl();
} }
@ -92,10 +85,6 @@ class VideoTest extends BaseTest
*/ */
public function testgetUrlWithWrongPassword() public function testgetUrlWithWrongPassword()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$video = new Video('http://vimeo.com/68375962', 'best', 'foo'); $video = new Video('http://vimeo.com/68375962', 'best', 'foo');
$video->getUrl(); $video->getUrl();
} }
@ -142,29 +131,19 @@ class VideoTest extends BaseTest
'flv', 'flv',
'bbcodspdns.fcod.llnwd.net', 'bbcodspdns.fcod.llnwd.net',
], ],
[
'http://www.rtl2.de/sendung/grip-das-motormagazin/folge/folge-203-0', 'bestaudio/best',
'GRIP_sucht_den_Sommerkonig-folge-203-0',
'f4v',
'edgefcs.net',
],
[ [
'https://openload.co/f/kUEfGclsU9o', 'best[protocol^=http]', 'https://openload.co/f/kUEfGclsU9o', 'best[protocol^=http]',
'skyrim_no-audio_1080.mp4-kUEfGclsU9o', 'skyrim_no-audio_1080.mp4-kUEfGclsU9o',
'mp4', 'mp4',
'openload.co', 'openload.co',
], ],
]; [
if (!getenv('CI')) {
// Travis is blacklisted by Vimeo.
$videos[] = [
'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',
]
]; ];
}
return $videos; return $videos;
} }
@ -193,17 +172,14 @@ class VideoTest extends BaseTest
*/ */
public function m3uUrlProvider() public function m3uUrlProvider()
{ {
$videos = []; $videos = [
[
if (!getenv('CI')) {
// Twitter returns a 429 error when the test is ran too many times.
$videos[] = [
'https://twitter.com/verge/status/813055465324056576/video/1', 'hls-2176', 'https://twitter.com/verge/status/813055465324056576/video/1', 'hls-2176',
'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',
]
]; ];
}
return $videos; return $videos;
} }
@ -365,10 +341,6 @@ class VideoTest extends BaseTest
*/ */
public function testGetAudioStreamDashError() public function testGetAudioStreamDashError()
{ {
if (getenv('CI')) {
$this->markTestSkipped('Travis is blacklisted by Vimeo.');
}
$video = new Video('https://vimeo.com/251997032', 'bestaudio/best'); $video = new Video('https://vimeo.com/251997032', 'bestaudio/best');
$video->getAudioStream(); $video->getAudioStream();
} }

View file

@ -6,6 +6,7 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\LocaleManager;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\Container; use Slim\Container;
use Slim\Http\Environment; use Slim\Http\Environment;
@ -24,7 +25,9 @@ class ViewFactoryTest extends BaseTest
*/ */
public function testCreate() public function testCreate()
{ {
$view = ViewFactory::create(new Container()); $container = new Container();
$container['locale'] = LocaleManager::getInstance();
$view = ViewFactory::create($container);
$this->assertInstanceOf(Smarty::class, $view); $this->assertInstanceOf(Smarty::class, $view);
} }
@ -35,8 +38,10 @@ class ViewFactoryTest extends BaseTest
*/ */
public function testCreateWithXForwardedProto() public function testCreateWithXForwardedProto()
{ {
$container = new Container();
$container['locale'] = LocaleManager::getInstance();
$request = Request::createFromEnvironment(Environment::mock()); $request = Request::createFromEnvironment(Environment::mock());
$view = ViewFactory::create(new Container(), $request->withHeader('X-Forwarded-Proto', 'https')); $view = ViewFactory::create($container, $request->withHeader('X-Forwarded-Proto', 'https'));
$this->assertInstanceOf(Smarty::class, $view); $this->assertInstanceOf(Smarty::class, $view);
} }
} }

View file

@ -11,6 +11,7 @@ use Alltube\Video;
/** /**
* Unit tests for the YoutubeChunkStream class. * Unit tests for the YoutubeChunkStream class.
* @requires download
*/ */
class YoutubeChunkStreamTest extends StreamTest class YoutubeChunkStreamTest extends StreamTest
{ {

View file

@ -11,6 +11,7 @@ use Alltube\Video;
/** /**
* Unit tests for the YoutubeStream class. * Unit tests for the YoutubeStream class.
* @requires download
*/ */
class YoutubeStreamTest extends StreamTest class YoutubeStreamTest extends StreamTest
{ {