Merge branch 'release-3.0.0'

This commit is contained in:
Pierre Rudloff 2021-04-20 23:16:45 +02:00
commit eeda434b2f
35 changed files with 846 additions and 1066 deletions

View file

@ -1,7 +1,5 @@
# AllTube Download # AllTube Download
[![Donate using Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Rudloff/donate)
HTML GUI for youtube-dl ([alltubedownload.net](http://alltubedownload.net/)) HTML GUI for youtube-dl ([alltubedownload.net](http://alltubedownload.net/))
![Screenshot](img/screenshot.png "AllTube GUI screenshot") ![Screenshot](img/screenshot.png "AllTube GUI screenshot")

View file

@ -8,6 +8,7 @@ use Alltube\Controller\JsonController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException; use Alltube\Exception\DependencyException;
use Alltube\Factory\ConfigFactory; use Alltube\Factory\ConfigFactory;
use Alltube\Factory\DebugBarFactory;
use Alltube\Factory\LocaleManagerFactory; use Alltube\Factory\LocaleManagerFactory;
use Alltube\Factory\LoggerFactory; use Alltube\Factory\LoggerFactory;
use Alltube\Factory\SessionFactory; use Alltube\Factory\SessionFactory;
@ -16,6 +17,7 @@ use Alltube\Middleware\CspMiddleware;
use Alltube\Middleware\LinkHeaderMiddleware; use Alltube\Middleware\LinkHeaderMiddleware;
use Alltube\Middleware\LocaleMiddleware; use Alltube\Middleware\LocaleMiddleware;
use Alltube\Middleware\RouterPathMiddleware; use Alltube\Middleware\RouterPathMiddleware;
use DebugBar\DebugBarException;
use Slim\Container; use Slim\Container;
use SmartyException; use SmartyException;
@ -26,6 +28,7 @@ class App extends \Slim\App
* @throws ConfigException * @throws ConfigException
* @throws DependencyException * @throws DependencyException
* @throws SmartyException * @throws SmartyException
* @throws DebugBarException
*/ */
public function __construct() public function __construct()
{ {
@ -34,6 +37,8 @@ class App extends \Slim\App
/** @var Container $container */ /** @var Container $container */
$container = $this->getContainer(); $container = $this->getContainer();
$container['root_path'] = $this->getRootPath();
// Config. // Config.
$container['config'] = ConfigFactory::create($container); $container['config'] = ConfigFactory::create($container);
@ -43,12 +48,17 @@ class App extends \Slim\App
// Locales. // Locales.
$container['locale'] = LocaleManagerFactory::create($container); $container['locale'] = LocaleManagerFactory::create($container);
// Smarty.
$container['view'] = ViewFactory::create($container);
// Logger. // Logger.
$container['logger'] = LoggerFactory::create($container); $container['logger'] = LoggerFactory::create($container);
if ($container->get('config')->debug) {
// Debug bar.
$container['debugbar'] = DebugBarFactory::create($container);
}
// Smarty.
$container['view'] = ViewFactory::create($container);
// Middlewares. // Middlewares.
$this->add(new LocaleMiddleware($container)); $this->add(new LocaleMiddleware($container));
$this->add(new CspMiddleware($container)); $this->add(new CspMiddleware($container));
@ -102,4 +112,17 @@ class App extends \Slim\App
[$jsonController, 'json'] [$jsonController, 'json']
)->setName('json'); )->setName('json');
} }
/**
* @return string|null
*/
private function getRootPath(): ?string
{
// realpath() can return false but we prefer using null.
if ($rootPath = realpath(__DIR__ . '/../')) {
return $rootPath;
}
return null;
}
} }

View file

@ -187,7 +187,7 @@ class Config
* *
* @return string * @return string
*/ */
public static function addHttpToFormat(string $format) public static function addHttpToFormat(string $format): string
{ {
$newFormat = []; $newFormat = [];
foreach (explode('/', $format) as $subformat) { foreach (explode('/', $format) as $subformat) {
@ -266,7 +266,7 @@ class Config
* @return Config * @return Config
* @throws ConfigException * @throws ConfigException
*/ */
public static function fromFile(string $file) public static function fromFile(string $file): Config
{ {
if (is_file($file)) { if (is_file($file)) {
return new self(Yaml::parse(strval(file_get_contents($file)))); return new self(Yaml::parse(strval(file_get_contents($file))));
@ -293,7 +293,7 @@ class Config
* *
* @return Downloader * @return Downloader
*/ */
public function getDownloader() public function getDownloader(): Downloader
{ {
return new Downloader( return new Downloader(
$this->youtubedl, $this->youtubedl,
@ -308,7 +308,7 @@ class Config
/** /**
* @return string * @return string
*/ */
public function getAppVersion() public function getAppVersion(): string
{ {
$version = PrettyVersions::getRootPackageVersion(); $version = PrettyVersions::getRootPackageVersion();

View file

@ -11,8 +11,8 @@ use Alltube\Library\Downloader;
use Alltube\Library\Video; use Alltube\Library\Video;
use Alltube\LocaleManager; use Alltube\LocaleManager;
use Aura\Session\Segment; use Aura\Session\Segment;
use Consolidation\Log\Logger;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
use Slim\Router; use Slim\Router;
@ -72,7 +72,7 @@ abstract class BaseController
protected $downloader; protected $downloader;
/** /**
* @var Logger * @var LoggerInterface
*/ */
protected $logger; protected $logger;
@ -111,7 +111,7 @@ abstract class BaseController
* *
* @return string format * @return string format
*/ */
protected function getFormat(Request $request) protected function getFormat(Request $request): string
{ {
$format = $request->getQueryParam('format'); $format = $request->getQueryParam('format');
if (!isset($format)) { if (!isset($format)) {
@ -126,9 +126,9 @@ abstract class BaseController
* *
* @param Request $request PSR-7 request * @param Request $request PSR-7 request
* *
* @return string Password * @return string|null Password
*/ */
protected function getPassword(Request $request) protected function getPassword(Request $request): ?string
{ {
$url = $request->getQueryParam('url'); $url = $request->getQueryParam('url');
@ -151,7 +151,7 @@ abstract class BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function displayError(Request $request, Response $response, string $message) protected function displayError(Request $request, Response $response, string $message): Response
{ {
$controller = new FrontController($this->container); $controller = new FrontController($this->container);

View file

@ -38,7 +38,7 @@ class DownloadController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
public function download(Request $request, Response $response) public function download(Request $request, Response $response): Response
{ {
$url = $request->getQueryParam('url'); $url = $request->getQueryParam('url');
@ -99,7 +99,7 @@ class DownloadController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
private function getConvertedAudioResponse(Request $request, Response $response) private function getConvertedAudioResponse(Request $request, Response $response): Response
{ {
$from = null; $from = null;
$to = null; $to = null;
@ -135,7 +135,7 @@ class DownloadController extends BaseController
* @throws PasswordException * @throws PasswordException
* @throws WrongPasswordException * @throws WrongPasswordException
*/ */
private function getAudioResponse(Request $request, Response $response) private function getAudioResponse(Request $request, Response $response): Response
{ {
if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) {
// Force convert when we need to seek. // Force convert when we need to seek.
@ -174,7 +174,7 @@ class DownloadController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
private function getStream(Request $request, Response $response) private function getStream(Request $request, Response $response): Response
{ {
if (isset($this->video->entries)) { if (isset($this->video->entries)) {
if ($this->config->convert && $request->getQueryParam('audio')) { if ($this->config->convert && $request->getQueryParam('audio')) {
@ -240,7 +240,7 @@ class DownloadController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
private function getRemuxStream(Request $request, Response $response) private function getRemuxStream(Request $request, Response $response): Response
{ {
if (!$this->config->remux) { if (!$this->config->remux) {
throw new RemuxException('You need to enable remux mode to merge two formats.'); throw new RemuxException('You need to enable remux mode to merge two formats.');
@ -267,7 +267,7 @@ class DownloadController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
private function getDownloadResponse(Request $request, Response $response) private function getDownloadResponse(Request $request, Response $response): Response
{ {
try { try {
$videoUrls = $this->video->getUrl(); $videoUrls = $this->video->getUrl();
@ -306,7 +306,7 @@ class DownloadController extends BaseController
* @throws YoutubedlException * @throws YoutubedlException
* @throws PopenStreamException * @throws PopenStreamException
*/ */
private function getConvertedResponse(Request $request, Response $response) private function getConvertedResponse(Request $request, Response $response): Response
{ {
$response = $response->withHeader( $response = $response->withHeader(
'Content-Disposition', 'Content-Disposition',

View file

@ -52,7 +52,7 @@ class FrontController extends BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
public function index(Request $request, Response $response) public function index(Request $request, Response $response): Response
{ {
$this->view->render( $this->view->render(
$response, $response,
@ -78,7 +78,7 @@ class FrontController extends BaseController
* *
* @return Response * @return Response
*/ */
public function locale(Request $request, Response $response, array $data) public function locale(Request $request, Response $response, array $data): Response
{ {
$this->localeManager->setLocale(new Locale($data['locale'])); $this->localeManager->setLocale(new Locale($data['locale']));
@ -94,7 +94,7 @@ class FrontController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
public function extractors(Request $request, Response $response) public function extractors(Request $request, Response $response): Response
{ {
$this->view->render( $this->view->render(
$response, $response,
@ -119,7 +119,7 @@ class FrontController extends BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
public function password(Request $request, Response $response) public function password(Request $request, Response $response): Response
{ {
$this->view->render( $this->view->render(
$response, $response,
@ -199,7 +199,7 @@ class FrontController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
public function info(Request $request, Response $response) public function info(Request $request, Response $response): Response
{ {
$url = $request->getQueryParam('url') ?: $request->getQueryParam('v'); $url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
@ -228,7 +228,7 @@ class FrontController extends BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function displayError(Request $request, Response $response, string $message) protected function displayError(Request $request, Response $response, string $message): Response
{ {
$this->view->render( $this->view->render(
$response, $response,
@ -248,7 +248,7 @@ class FrontController extends BaseController
* @param Response $response * @param Response $response
* @return Response * @return Response
*/ */
public function notFound(Request $request, Response $response) public function notFound(Request $request, Response $response): Response
{ {
return $this->displayError($request, $response, $this->localeManager->t('Page not found')) return $this->displayError($request, $response, $this->localeManager->t('Page not found'))
->withStatus(StatusCode::HTTP_NOT_FOUND); ->withStatus(StatusCode::HTTP_NOT_FOUND);
@ -259,7 +259,7 @@ class FrontController extends BaseController
* @param Response $response * @param Response $response
* @return Response * @return Response
*/ */
public function notAllowed(Request $request, Response $response) public function notAllowed(Request $request, Response $response): Response
{ {
return $this->displayError($request, $response, $this->localeManager->t('Method not allowed')) return $this->displayError($request, $response, $this->localeManager->t('Method not allowed'))
->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED); ->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED);
@ -274,7 +274,7 @@ class FrontController extends BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
public function error(Request $request, Response $response, Throwable $error) public function error(Request $request, Response $response, Throwable $error): Response
{ {
$this->logger->error($error); $this->logger->error($error);
@ -285,7 +285,7 @@ class FrontController extends BaseController
$response = $cspMiddleware->applyHeader($response); $response = $cspMiddleware->applyHeader($response);
if ($this->config->debug) { if ($this->config->debug) {
$renderer = new HtmlErrorRenderer(true); $renderer = new HtmlErrorRenderer(true, null, null, $this->container->get('root_path'));
$exception = $renderer->render($error); $exception = $renderer->render($error);
$response->getBody()->write($exception->getAsString()); $response->getBody()->write($exception->getAsString());

View file

@ -25,7 +25,7 @@ class JsonController extends BaseController
* @return Response HTTP response * @return Response HTTP response
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
public function json(Request $request, Response $response) public function json(Request $request, Response $response): Response
{ {
$url = $request->getQueryParam('url'); $url = $request->getQueryParam('url');

View file

@ -23,7 +23,7 @@ class ErrorHandler
if (class_exists(HtmlErrorRenderer::class)) { if (class_exists(HtmlErrorRenderer::class)) {
// If dev dependencies are loaded, we can use symfony/error-handler. // If dev dependencies are loaded, we can use symfony/error-handler.
$renderer = new HtmlErrorRenderer(true); $renderer = new HtmlErrorRenderer(true, null, null, dirname(__DIR__));
$exception = $renderer->render($e); $exception = $renderer->render($e);
http_response_code($exception->getStatusCode()); http_response_code($exception->getStatusCode());

View file

@ -20,9 +20,9 @@ class ConfigFactory
* @return Config * @return Config
* @throws ConfigException * @throws ConfigException
*/ */
public static function create(Container $container) public static function create(Container $container): Config
{ {
$configPath = __DIR__ . '/../../config/config.yml'; $configPath = $container->get('root_path') . '/config/config.yml';
if (is_file($configPath)) { if (is_file($configPath)) {
$config = Config::fromFile($configPath); $config = Config::fromFile($configPath);
} else { } else {

View file

@ -0,0 +1,48 @@
<?php
namespace Alltube\Factory;
use DebugBar\DataCollector\ConfigCollector;
use DebugBar\DataCollector\MemoryCollector;
use DebugBar\DataCollector\MessagesCollector;
use DebugBar\DataCollector\PhpInfoCollector;
use DebugBar\DataCollector\RequestDataCollector;
use DebugBar\DebugBar;
use DebugBar\DebugBarException;
use Kitchenu\Debugbar\DataCollector\SlimRouteCollector;
use Slim\Container;
/**
* Class DebugBarFactory
* @package Alltube\Factory
*/
class DebugBarFactory
{
/**
* @param Container $container
* @return DebugBar
* @throws DebugBarException
*/
public static function create(Container $container): DebugBar
{
$debugBar = new DebugBar();
$requestCollector = new RequestDataCollector();
$configCollector = new ConfigCollector(get_object_vars($container->get('config')));
$debugBar->addCollector(new PhpInfoCollector())
->addCollector(new MessagesCollector())
->addCollector($requestCollector)
->addCollector(new MemoryCollector())
->addCollector($configCollector)
->addCollector(new SlimRouteCollector($container->get('router'), $container->get('request')));
$container->get('logger')->add('debugbar', $debugBar->getCollector('messages'));
$requestCollector->useHtmlVarDumper();
$configCollector->useHtmlVarDumper();
return $debugBar;
}
}

View file

@ -15,10 +15,10 @@ class LocaleManagerFactory
/** /**
* @param Container $container * @param Container $container
* @return LocaleManager|null * @return LocaleManager
* @throws DependencyException * @throws DependencyException
*/ */
public static function create(Container $container) public static function create(Container $container): LocaleManager
{ {
if (!class_exists('Locale')) { if (!class_exists('Locale')) {
throw new DependencyException('You need to install the intl extension for PHP.'); throw new DependencyException('You need to install the intl extension for PHP.');

View file

@ -3,6 +3,7 @@
namespace Alltube\Factory; namespace Alltube\Factory;
use Consolidation\Log\Logger; use Consolidation\Log\Logger;
use Consolidation\Log\LoggerManager;
use Consolidation\Log\LogOutputStyler; use Consolidation\Log\LogOutputStyler;
use Slim\Container; use Slim\Container;
use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutput;
@ -16,9 +17,9 @@ class LoggerFactory
/** /**
* @param Container $container * @param Container $container
* @return Logger * @return LoggerManager
*/ */
public static function create(Container $container) public static function create(Container $container): LoggerManager
{ {
$config = $container->get('config'); $config = $container->get('config');
if ($config->debug) { if ($config->debug) {
@ -27,9 +28,13 @@ class LoggerFactory
$verbosity = ConsoleOutput::VERBOSITY_NORMAL; $verbosity = ConsoleOutput::VERBOSITY_NORMAL;
} }
$loggerManager = new LoggerManager();
$logger = new Logger(new ConsoleOutput($verbosity)); $logger = new Logger(new ConsoleOutput($verbosity));
$logger->setLogOutputStyler(new LogOutputStyler()); $logger->setLogOutputStyler(new LogOutputStyler());
return $logger; $loggerManager->add('default', $logger);
return $loggerManager;
} }
} }

View file

@ -21,7 +21,7 @@ class SessionFactory
* @param Container $container * @param Container $container
* @return Session * @return Session
*/ */
public static function create(Container $container) public static function create(Container $container): Session
{ {
$session_factory = new \Aura\Session\SessionFactory(); $session_factory = new \Aura\Session\SessionFactory();
$session = $session_factory->newInstance($_COOKIE); $session = $session_factory->newInstance($_COOKIE);

View file

@ -7,6 +7,7 @@
namespace Alltube\Factory; namespace Alltube\Factory;
use Alltube\LocaleManager; use Alltube\LocaleManager;
use Junker\DebugBar\Bridge\SmartyCollector;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Uri; use Slim\Http\Uri;
@ -26,7 +27,7 @@ class ViewFactory
* *
* @return string URL * @return string URL
*/ */
private static function getCanonicalUrl(Request $request) private static function getCanonicalUrl(Request $request): string
{ {
/** @var Uri $uri */ /** @var Uri $uri */
$uri = $request->getUri(); $uri = $request->getUri();
@ -45,13 +46,13 @@ class ViewFactory
* @return Smarty * @return Smarty
* @throws SmartyException * @throws SmartyException
*/ */
public static function create(ContainerInterface $container, Request $request = null) public static function create(ContainerInterface $container, Request $request = null): Smarty
{ {
if (!isset($request)) { if (!isset($request)) {
$request = $container->get('request'); $request = $container->get('request');
} }
$view = new Smarty(__DIR__ . '/../../templates/'); $view = new Smarty($container->get('root_path') . '/templates/');
/** @var Uri $uri */ /** @var Uri $uri */
$uri = $request->getUri(); $uri = $request->getUri();
@ -85,6 +86,19 @@ class ViewFactory
$view->offsetSet('config', $container->get('config')); $view->offsetSet('config', $container->get('config'));
$view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl()); $view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl());
if ($container->has('debugbar')) {
$debugBar = $container->get('debugbar');
$debugBar->addCollector(new SmartyCollector($view->getSmarty()));
$view->offsetSet(
'debug_render',
$debugBar->getJavascriptRenderer(
$uri->getBaseUrl() . '/vendor/maximebf/debugbar/src/DebugBar/Resources/'
)
);
}
return $view; return $view;
} }
} }

View file

@ -48,7 +48,7 @@ class Locale
* *
* @return string ISO 15897 code * @return string ISO 15897 code
*/ */
public function __toString() public function __toString(): string
{ {
return $this->getIso15897(); return $this->getIso15897();
} }
@ -58,7 +58,7 @@ class Locale
* *
* @return string * @return string
*/ */
public function getFullName() public function getFullName(): string
{ {
return PHPLocale::getDisplayName($this->getIso15897(), $this->getIso15897()); return PHPLocale::getDisplayName($this->getIso15897(), $this->getIso15897());
} }
@ -68,7 +68,7 @@ class Locale
* *
* @return string * @return string
*/ */
public function getIso15897() public function getIso15897(): string
{ {
if (isset($this->region)) { if (isset($this->region)) {
return $this->language . '_' . $this->region; return $this->language . '_' . $this->region;
@ -82,7 +82,7 @@ class Locale
* *
* @return string * @return string
*/ */
public function getBcp47() public function getBcp47(): string
{ {
if (isset($this->region)) { if (isset($this->region)) {
return $this->language . '-' . $this->region; return $this->language . '-' . $this->region;
@ -96,7 +96,7 @@ class Locale
* *
* @return string * @return string
*/ */
public function getIso3166() public function getIso3166(): string
{ {
return strtolower($this->region); return strtolower($this->region);
} }

View file

@ -80,7 +80,7 @@ class LocaleManager
* *
* @return Locale[] * @return Locale[]
*/ */
public function getSupportedLocales() public function getSupportedLocales(): array
{ {
$return = [ $return = [
new Locale('en_US') new Locale('en_US')
@ -103,7 +103,7 @@ class LocaleManager
* *
* @return Locale|null * @return Locale|null
*/ */
public function getLocale() public function getLocale(): ?Locale
{ {
return $this->curLocale; return $this->curLocale;
} }
@ -140,7 +140,7 @@ class LocaleManager
* *
* @return string Translated string * @return string Translated string
*/ */
public function smartyTranslate(array $params, string $text = null) public function smartyTranslate(array $params, string $text = null): string
{ {
if (isset($params['params'])) { if (isset($params['params'])) {
return $this->t($text, $params['params']); return $this->t($text, $params['params']);
@ -157,7 +157,7 @@ class LocaleManager
* @param mixed[] $params * @param mixed[] $params
* @return string Translated string * @return string Translated string
*/ */
public function t(string $string = null, array $params = []) public function t(string $string = null, array $params = []): string
{ {
if (isset($string)) { if (isset($string)) {
return $this->translator->trans($string, $params); return $this->translator->trans($string, $params);

View file

@ -34,10 +34,11 @@ class CspMiddleware
* @param Response $response * @param Response $response
* @return MessageInterface * @return MessageInterface
*/ */
public function applyHeader(Response $response) public function applyHeader(Response $response): MessageInterface
{ {
$csp = new CSPBuilder(); $csp = new CSPBuilder();
$csp->addDirective('default-src', []) $csp->disableOldBrowserSupport()
->addDirective('default-src', [])
->addDirective('font-src', ['self' => true]) ->addDirective('font-src', ['self' => true])
->addDirective('style-src', ['self' => true]) ->addDirective('style-src', ['self' => true])
->addDirective('manifest-src', ['self' => true]) ->addDirective('manifest-src', ['self' => true])
@ -47,9 +48,10 @@ class CspMiddleware
->addSource('img-src', '*'); ->addSource('img-src', '*');
if ($this->config->debug) { if ($this->config->debug) {
// So symfony/debug and symfony/error-handler can work. // So maximebf/debugbar, symfony/debug and symfony/error-handler can work.
$csp->setDirective('script-src', ['unsafe-inline' => true]) $csp->setDirective('script-src', ['self' => true, 'unsafe-inline' => true])
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true]); ->setDirective('style-src', ['self' => true, 'unsafe-inline' => true])
->addSource('img-src', 'data:');
} }
return $csp->injectCSPHeader($response); return $csp->injectCSPHeader($response);

View file

@ -42,7 +42,7 @@ class LocaleMiddleware
* *
* @return Locale|null Locale if chosen, nothing otherwise * @return Locale|null Locale if chosen, nothing otherwise
*/ */
public function testLocale(array $proposedLocale) public function testLocale(array $proposedLocale): ?Locale
{ {
foreach ($this->localeManager->getSupportedLocales() as $locale) { foreach ($this->localeManager->getSupportedLocales() as $locale) {
$parsedLocale = AcceptLanguage::parse($locale); $parsedLocale = AcceptLanguage::parse($locale);
@ -67,7 +67,7 @@ class LocaleMiddleware
* *
* @return Response * @return Response
*/ */
public function __invoke(Request $request, Response $response, callable $next) public function __invoke(Request $request, Response $response, callable $next): Response
{ {
$headers = $request->getHeader('Accept-Language'); $headers = $request->getHeader('Accept-Language');
$curLocale = $this->localeManager->getLocale(); $curLocale = $this->localeManager->getLocale();

View file

@ -113,7 +113,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return int|null * @return int|null
*/ */
public function getSize() public function getSize(): ?int
{ {
return null; return null;
} }
@ -123,7 +123,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isSeekable() public function isSeekable(): bool
{ {
return true; return true;
} }
@ -143,7 +143,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isWritable() public function isWritable(): bool
{ {
return true; return true;
} }
@ -153,7 +153,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isReadable() public function isReadable(): bool
{ {
return true; return true;
} }
@ -173,7 +173,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @param string|null $key string $key Specific metadata to retrieve. * @param string|null $key string $key Specific metadata to retrieve.
* *
* @return array|mixed|null * @return mixed|null
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)
{ {
@ -208,7 +208,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return string * @return string
*/ */
public function __toString() public function __toString(): string
{ {
$this->rewind(); $this->rewind();
@ -243,7 +243,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function eof() public function eof(): bool
{ {
return $this->isComplete && feof($this->buffer); return $this->isComplete && feof($this->buffer);
} }
@ -272,12 +272,12 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Read data from the stream. * Read data from the stream.
* *
* @param mixed $count Number of bytes to read * @param mixed $length Number of bytes to read
* *
* @return string|false * @return string|false
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
*/ */
public function read($count) public function read($length)
{ {
// If the archive is complete, we only read the remaining buffer. // If the archive is complete, we only read the remaining buffer.
if (!$this->isComplete) { if (!$this->isComplete) {
@ -297,15 +297,22 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
} }
} else { } else {
// Continue streaming the current video. // Continue streaming the current video.
$this->stream_file_part($this->curVideoStream->read($count)); $this->stream_file_part($this->curVideoStream->read($length));
} }
} else { } else {
// Start streaming the first video. // Start streaming the first video.
$this->startVideoStream(current($this->videos)); $video = current($this->videos);
if ($video) {
$this->startVideoStream($video);
} else {
$this->push_error('Playlist was empty');
$this->finish();
$this->isComplete = true;
}
} }
} }
return fread($this->buffer, $count); return fread($this->buffer, $length);
} }
/** /**

View file

@ -39,7 +39,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return string * @return string
*/ */
public function read($length) public function read($length): string
{ {
$size = intval($this->response->getHeader('Content-Length')[0]); $size = intval($this->response->getHeader('Content-Length')[0]);
if ($size - $this->tell() < $length) { if ($size - $this->tell() < $length) {
@ -53,7 +53,7 @@ class YoutubeChunkStream implements StreamInterface
/** /**
* Reads all data from the stream into a string, from the beginning to end. * Reads all data from the stream into a string, from the beginning to end.
*/ */
public function __toString() public function __toString(): string
{ {
return (string)$this->response->getBody(); return (string)$this->response->getBody();
} }
@ -83,7 +83,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return int|null * @return int|null
*/ */
public function getSize() public function getSize(): ?int
{ {
return $this->response->getBody()->getSize(); return $this->response->getBody()->getSize();
} }
@ -93,7 +93,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return int * @return int
*/ */
public function tell() public function tell(): int
{ {
return $this->response->getBody()->tell(); return $this->response->getBody()->tell();
} }
@ -103,7 +103,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function eof() public function eof(): bool
{ {
return $this->response->getBody()->eof(); return $this->response->getBody()->eof();
} }
@ -113,7 +113,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isSeekable() public function isSeekable(): bool
{ {
return $this->response->getBody()->isSeekable(); return $this->response->getBody()->isSeekable();
} }
@ -146,7 +146,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isWritable() public function isWritable(): bool
{ {
return $this->response->getBody()->isWritable(); return $this->response->getBody()->isWritable();
} }
@ -168,7 +168,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return bool * @return bool
*/ */
public function isReadable() public function isReadable(): bool
{ {
return $this->response->getBody()->isReadable(); return $this->response->getBody()->isReadable();
} }
@ -178,7 +178,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @return string * @return string
*/ */
public function getContents() public function getContents(): string
{ {
return $this->response->getBody()->getContents(); return $this->response->getBody()->getContents();
} }
@ -188,7 +188,7 @@ class YoutubeChunkStream implements StreamInterface
* *
* @param string|null $key Specific metadata to retrieve. * @param string|null $key Specific metadata to retrieve.
* *
* @return array|mixed|null * @return mixed|null
*/ */
public function getMetadata($key = null) public function getMetadata($key = null)
{ {

View file

@ -26,7 +26,7 @@ class UglyRouter extends Router
* *
* @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php * @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
*/ */
public function dispatch(ServerRequestInterface $request) public function dispatch(ServerRequestInterface $request): array
{ {
$params = $request->getQueryParams(); $params = $request->getQueryParams();
$uri = new Uri('', ''); $uri = new Uri('', '');
@ -53,7 +53,7 @@ class UglyRouter extends Router
* @throws InvalidArgumentException If required data not provided * @throws InvalidArgumentException If required data not provided
* @throws RuntimeException If named route does not exist * @throws RuntimeException If named route does not exist
*/ */
public function pathFor($name, array $data = [], array $queryParams = []) public function pathFor($name, array $data = [], array $queryParams = []): string
{ {
$queryParams['page'] = $name; $queryParams['page'] = $name;
$url = Uri::createFromString($this->relativePathFor($name, $data, $queryParams))->withPath(''); $url = Uri::createFromString($this->relativePathFor($name, $data, $queryParams))->withPath('');

View file

@ -36,18 +36,21 @@
"symfony/translation": "^4.0", "symfony/translation": "^4.0",
"symfony/yaml": "^4.0", "symfony/yaml": "^4.0",
"webfontkit/open-sans": "^1.0", "webfontkit/open-sans": "^1.0",
"ytdl-org/youtube-dl": "^2020.11", "ytdl-org/youtube-dl": "^2021.04",
"zonuexe/http-accept-language": "^0.4.1" "zonuexe/http-accept-language": "^0.4.1"
}, },
"require-dev": { "require-dev": {
"consolidation/robo": "^2.1", "consolidation/robo": "^2.1",
"enlightn/security-checker": "^1.4",
"ergebnis/composer-normalize": "^2.6", "ergebnis/composer-normalize": "^2.6",
"insite/composer-dangling-locked-deps": "^0.2.1", "insite/composer-dangling-locked-deps": "^0.2.1",
"junker/debugbar-smarty": "^0.1.0",
"kitchenu/slim-debugbar": "^1.1",
"maximebf/debugbar": "^1.16",
"php-mock/php-mock-mockery": "^1.3", "php-mock/php-mock-mockery": "^1.3",
"phpro/grumphp": "^1.1", "phpro/grumphp": "^1.3",
"phpstan/phpstan": "^0.12.25", "phpstan/phpstan": "^0.12.72",
"phpunit/phpunit": "^8.4", "phpunit/phpunit": "^8.4",
"sensiolabs/security-checker": "^6.0",
"smarty-gettext/smarty-gettext": "^1.6", "smarty-gettext/smarty-gettext": "^1.6",
"squizlabs/php_codesniffer": "^3.5", "squizlabs/php_codesniffer": "^3.5",
"symfony/error-handler": "^5.0", "symfony/error-handler": "^5.0",
@ -87,10 +90,10 @@
"type": "package", "type": "package",
"package": { "package": {
"name": "ytdl-org/youtube-dl", "name": "ytdl-org/youtube-dl",
"version": "2020.11.12", "version": "2021.04.01",
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://github.com/ytdl-org/youtube-dl/archive/2020.11.12.zip" "url": "https://github.com/ytdl-org/youtube-dl/archive/2021.04.01.zip"
} }
} }
} }

1342
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ grumphp:
xmllint: ~ xmllint: ~
yamllint: ~ yamllint: ~
composer: ~ composer: ~
securitychecker: ~ securitychecker_enlightn: ~
composer_normalize: ~ composer_normalize: ~
composer_dangling_locked_deps: ~ composer_dangling_locked_deps: ~
phpcs: phpcs:

View file

@ -0,0 +1,218 @@
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: POEditor.com\n"
"Project-Id-Version: Alltube Download\n"
"Language: ja-JP\n"
#: templates/inc/footer.tpl:8
msgid "Code by @dev"
msgstr "作成: @dev"
#: templates/inc/footer.tpl:16
msgid "Design by @designer"
msgstr "デザイン: @designer"
#: templates/inc/footer.tpl:21
msgid "Get the code"
msgstr "プログラムをダウンロード"
#: templates/inc/footer.tpl:29
msgid "Based on @youtubedl"
msgstr "本ソフトは@youtubedlを基に構成されています。"
#: templates/inc/footer.tpl:33
msgid "Donate using Liberapay"
msgstr "Liberapayで寄付"
#: templates/inc/footer.tpl:35
msgid "Donate"
msgstr "寄付"
#: templates/inc/header.tpl:4
msgid "Switch language"
msgstr "言語の変更"
#: templates/inc/header.tpl:8
msgid "Set language"
msgstr "言語を設定"
#: templates/info.tpl:11
msgid "You are going to download @title."
msgstr "ダウンロード対象: @title"
#: templates/info.tpl:29
msgid "Available formats:"
msgstr "ダウンロード可能な形式"
#: templates/info.tpl:31
msgid "Generic formats"
msgstr "よく使われる形式"
#: templates/info.tpl:35
msgid "Best"
msgstr "最高品質"
#: templates/info.tpl:36
msgid "Remux best video with best audio"
msgstr "最高品質に再エンコード"
#: templates/info.tpl:37
msgid "Worst"
msgstr "最低品質"
#: templates/info.tpl:42
msgid "Detailed formats"
msgstr "その他の形式"
#: templates/info.tpl:86
msgid "Stream the video through the server"
msgstr "ビデオをサーバーに設置してダウンロード"
#: templates/info.tpl:92
msgid "Convert into a custom format:"
msgstr "指定した形式への変換"
#: templates/info.tpl:93
msgid "Custom format"
msgstr "カスタム形式"
#: templates/info.tpl:93
msgid "Format to convert to"
msgstr "ファイル形式の変換"
#: templates/info.tpl:98
msgid "with"
msgstr "と"
#: templates/info.tpl:99
msgid "Bit rate"
msgstr "ビットレート"
#: templates/info.tpl:100
msgid "Custom bitrate"
msgstr "カスタム・ビットレート"
#: templates/info.tpl:103
msgid "kbit/s audio"
msgstr "kbps 音声"
#: templates/info.tpl:107 templates/playlist.tpl:38 templates/password.tpl:11
#: templates/index.tpl:19
msgid "Download"
msgstr "ダウンロード"
#: templates/playlist.tpl:12
msgid "Videos extracted from @title:"
msgstr "デコードの対象: @title"
#: templates/playlist.tpl:39
msgid "More options"
msgstr "詳細設定"
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:111
msgid "Supported websites"
msgstr "ダウンロードに対応しているサイト"
#: templates/error.tpl:5
msgid "An error occurred"
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/index.tpl:8
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
msgstr "動画のリンク(URL)を入力欄に入力してください。(例:Youtube,Dailymotion等。)"
#: templates/index.tpl:25
msgid "Audio only (MP3)"
msgstr "音声のみのダウンロード(mp3形式)"
#: templates/index.tpl:29
msgid "From"
msgstr "から"
#: templates/index.tpl:32
msgid "to"
msgstr "へ"
#: templates/index.tpl:41
msgid "See all supported websites"
msgstr "ダウンロード可能なサイトを見る"
#: templates/index.tpl:43
msgid "Drag this to your bookmarks bar:"
msgstr "ブックマークバーにドラッグして登録"
#: templates/index.tpl:45
msgid "Bookmarklet"
msgstr "ブックマークボタン"
#: classes/Controller/DownloadController.php:64
#: classes/Controller/FrontController.php:166
msgid "Wrong password"
msgstr "パスワードが間違っています。"
#: classes/Controller/DownloadController.php:69
msgid "Conversion of playlists is not supported."
msgstr "プレイリストからの読み出しには対応しておりません。"
#: classes/Controller/DownloadController.php:76
msgid "Conversion of M3U8 files is not supported."
msgstr "HLS(m3u8)プレイリストファイルからの読み出しには対応しておりません。"
#: classes/Controller/DownloadController.php:82
msgid "Conversion of DASH segments is not supported."
msgstr "MPEG DASHストリーミングからの読み出しには対応しておりません。"
#: classes/Controller/FrontController.php:65
msgid "Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
msgstr "Youtubeから動画を簡単にダウンロード!、DailymotionやVimeo等にも対応しております"
#: classes/Controller/FrontController.php:112
msgid "List of all supported websites from which AllTube Download can extract video or audio files"
msgstr "AllTube上でのダウンロードおよびファイルの変換に対応している音声または動画ファイルのサイト"
#: classes/Controller/FrontController.php:138
msgid "Password prompt"
msgstr "パスワード画面"
#: classes/Controller/FrontController.php:140
msgid "You need a password in order to download this video with AllTube Download"
msgstr "このビデオをAllTubeでダウンロードするにはパスワードが必要です。"
#: classes/Controller/FrontController.php:174
msgid "Video download"
msgstr "動画をダウンロード"
#: classes/Controller/FrontController.php:176
msgid "Download video from @extractor"
msgstr "@extractor からダウンロードします。"
#: classes/Controller/FrontController.php:182
msgid "Download @title from @extractor"
msgstr "@extractor から @title をダウンロードします"
#: classes/Controller/FrontController.php:255
msgid "Error"
msgstr "エラーが発生しました。"
#: classes/Controller/FrontController.php:271
msgid "Page not found"
msgstr "存在しないページです。"
#: classes/Controller/FrontController.php:282
msgid "Method not allowed"
msgstr "無効なリクエストです。"

View file

@ -10,8 +10,4 @@
<directory>tests/</directory> <directory>tests/</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<logging>
<log type="coverage-html" target="coverage/"/>
<log type="coverage-clover" target="clover.xml"/>
</logging>
</phpunit> </phpunit>

View file

@ -13,7 +13,6 @@ so it has low RAM and CPU.
AllTube probably won't switch to a more expensive hosting AllTube probably won't switch to a more expensive hosting
because this project does not earn any financial resources because this project does not earn any financial resources
(although [donations are welcome](https://liberapay.com/Rudloff/))
and you are encouraged to host it yourself. and you are encouraged to host it yourself.
## alltubedownload.net often says "An error occurred in the application…" ## alltubedownload.net often says "An error occurred in the application…"

View file

@ -27,15 +27,11 @@
youtube-dl youtube-dl
</a>"} </a>"}
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t} {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>
</div> </div>
{if isset($debug_render)}
{$debug_render->render()}
{/if}
</body> </body>
</html> </html>

View file

@ -22,6 +22,10 @@
<meta name="theme-color" content="#4F4F4F"/> <meta name="theme-color" content="#4F4F4F"/>
<link rel="manifest" href="{base_url}/resources/manifest.json"/> <link rel="manifest" href="{base_url}/resources/manifest.json"/>
<meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/> <meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/>
{if isset($debug_render)}
{$debug_render->renderHead()}
{/if}
</head> </head>
<body> <body>
<div class="page {$class}"> <div class="page {$class}">

View file

@ -18,7 +18,7 @@ abstract class BaseTest extends TestCase
* *
* @return string Path to file * @return string Path to file
*/ */
protected function getConfigFile() protected function getConfigFile(): string
{ {
return __DIR__ . '/../config/config_test.yml'; return __DIR__ . '/../config/config_test.yml';
} }

View file

@ -40,6 +40,7 @@ abstract class ContainerTest extends BaseTest
$this->checkRequirements(); $this->checkRequirements();
$this->container = new Container(['environment' => Environment::mock()]); $this->container = new Container(['environment' => Environment::mock()]);
$this->container['root_path'] = dirname(__DIR__);
$this->container['config'] = Config::fromFile($this->getConfigFile()); $this->container['config'] = Config::fromFile($this->getConfigFile());
$this->container['session'] = SessionFactory::create($this->container); $this->container['session'] = SessionFactory::create($this->container);
$this->container['locale'] = LocaleManagerFactory::create($this->container); $this->container['locale'] = LocaleManagerFactory::create($this->container);

View file

@ -13,6 +13,7 @@ use Alltube\Controller\FrontController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException; use Alltube\Exception\DependencyException;
use Slim\Http\Response; use Slim\Http\Response;
use Slim\Views\Smarty;
use SmartyException; use SmartyException;
/** /**
@ -52,6 +53,12 @@ abstract class ControllerTest extends ContainerTest
->setName('locale'); ->setName('locale');
$router->map(['GET'], '/redirect', [$downloadController, 'download']) $router->map(['GET'], '/redirect', [$downloadController, 'download'])
->setName('download'); ->setName('download');
/** @var Smarty $view */
$view = $this->container->get('view');
// Make sure we start the tests without compiled templates.
$view->getSmarty()->clearCompiledTemplate();
} }
/** /**
@ -62,7 +69,7 @@ abstract class ControllerTest extends ContainerTest
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function getRequestResult(string $request, array $params) protected function getRequestResult(string $request, array $params): Response
{ {
return $this->controller->$request( return $this->controller->$request(
$this->container->get('request')->withQueryParams($params), $this->container->get('request')->withQueryParams($params),

View file

@ -85,26 +85,32 @@ class LocaleMiddlewareTest extends ContainerTest
* Check that the request contains an Accept-Language header. * Check that the request contains an Accept-Language header.
* *
* @param Request $request PSR-7 request * @param Request $request PSR-7 request
* @param Response $response
* *
* @return void * @return Response
*/ */
public function assertHeader(Request $request) public function assertHeader(Request $request, Response $response): Response
{ {
$header = $request->getHeader('Accept-Language'); $header = $request->getHeader('Accept-Language');
$this->assertEquals('foo-BAR', $header[0]); $this->assertEquals('foo-BAR', $header[0]);
return $response;
} }
/** /**
* Check that the request contains no Accept-Language header. * Check that the request contains no Accept-Language header.
* *
* @param Request $request PSR-7 request * @param Request $request PSR-7 request
* @param Response $response
* *
* @return void * @return Response
*/ */
public function assertNoHeader(Request $request) public function assertNoHeader(Request $request, Response $response): Response
{ {
$header = $request->getHeader('Accept-Language'); $header = $request->getHeader('Accept-Language');
$this->assertEmpty($header); $this->assertEmpty($header);
return $response;
} }
/** /**

View file

@ -37,7 +37,16 @@ class UglyRouterTest extends ContainerTest
parent::setUp(); parent::setUp();
$this->router = new UglyRouter(); $this->router = new UglyRouter();
$this->router->map(['GET'], '/foo', 'print')->setName('foo'); $this->router->map(['GET'], '/foo', [$this, 'fakeHandler'])->setName('foo');
}
/**
* Empty function that only exists so that our route can have a handler.
*
* @return void
*/
private function fakeHandler()
{
} }
/** /**

View file

@ -157,7 +157,7 @@ class VideoTest extends ContainerTest
* *
* @return array[] * @return array[]
*/ */
public function urlProvider() public function urlProvider(): array
{ {
return [ return [
[ [
@ -193,7 +193,7 @@ class VideoTest extends ContainerTest
* *
* @return array[] * @return array[]
*/ */
public function remuxUrlProvider() public function remuxUrlProvider(): array
{ {
return [ return [
[ [
@ -210,7 +210,7 @@ class VideoTest extends ContainerTest
* *
* @return array[] * @return array[]
*/ */
public function m3uUrlProvider() public function m3uUrlProvider(): array
{ {
return [ return [
[ [
@ -227,7 +227,7 @@ class VideoTest extends ContainerTest
* *
* @return array[] * @return array[]
*/ */
public function rtmpUrlProvider() public function rtmpUrlProvider(): array
{ {
return [ return [
[ [
@ -244,7 +244,7 @@ class VideoTest extends ContainerTest
* *
* @return array[] * @return array[]
*/ */
public function errorUrlProvider() public function errorUrlProvider(): array
{ {
return [ return [
['http://example.com/video'], ['http://example.com/video'],