Merge branch 'release-3.0.0-beta2'

This commit is contained in:
Pierre Rudloff 2020-10-20 00:38:38 +02:00
commit e97573922d
57 changed files with 2933 additions and 1872 deletions

View File

@ -36,6 +36,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'; style-src 'self' 'unsafe-inline'; img-src http:" Header add Link "</css/style.css>; rel=preload" "expr=%{CONTENT_TYPE} =~ m#text/html#"
Header add Link "</css/fonts.css>; rel=preload, </css/style.css>; rel=preload" "expr=%{CONTENT_TYPE} =~ m#text/html#"
</ifmodule> </ifmodule>

View File

@ -76,6 +76,12 @@ You will need PHP 7.2 (or higher) and the following PHP modules:
## Web server configuration ## Web server configuration
If you want to serve the application under a basepath and/or with a different internal than external port (scenario: nginx->docker setup) Alltube supports the following X-Forwarded headers:
* X-Forwarded-Host (ex. `another.domain.com`)
* X-Forwarded-Path (ex: `/alltube`)
* X-Forwarded-Port (ex: `5555`)
### Apache ### Apache
The following modules are recommended: The following modules are recommended:

View File

@ -1,46 +0,0 @@
<?php
use Robo\Tasks;
/**
* Manage robo tasks.
*/
class RoboFile extends Tasks
{
/**
* Create release archive
* @return void
*/
public function release()
{
$this->stopOnFail();
$result = $this->taskExec('git')
->arg('describe')
->run();
$result->provideOutputdata();
$tmpDir = $this->_tmpDir();
$filename = 'alltube-' . trim($result->getOutputData()) . '.zip';
$this->taskFilesystemStack()
->remove($filename)
->run();
$this->taskGitStack()
->cloneRepo(__DIR__, $tmpDir)
->run();
$this->taskComposerInstall()
->dir($tmpDir)
->optimizeAutoloader()
->noDev()
->run();
$this->taskPack($filename)
->addDir('alltube', $tmpDir)
->run();
}
}

View File

@ -10,7 +10,6 @@ use Alltube\Exception\ConfigException;
use Alltube\Library\Downloader; use Alltube\Library\Downloader;
use Jawira\CaseConverter\CaseConverterException; use Jawira\CaseConverter\CaseConverterException;
use Jean85\PrettyVersions; use Jean85\PrettyVersions;
use PackageVersions\Versions;
use Symfony\Component\ErrorHandler\Debug; use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Jawira\CaseConverter\Convert; use Jawira\CaseConverter\Convert;
@ -20,12 +19,6 @@ use Jawira\CaseConverter\Convert;
*/ */
class Config class Config
{ {
/**
* Singleton instance.
*
* @var Config|null
*/
private static $instance;
/** /**
* youtube-dl binary path. * youtube-dl binary path.
@ -140,17 +133,32 @@ class Config
*/ */
public $debug = false; public $debug = false;
/**
* Default to audio.
*
* @var bool
*/
public $defaultAudio = false;
/**
* Disable audio conversion from/to seeker.
*
* @var bool
*/
public $convertSeek = true;
/** /**
* Config constructor. * Config constructor.
* *
* @param mixed[] $options Options * @param mixed[] $options Options
* @throws ConfigException * @throws ConfigException
*/ */
private function __construct(array $options = []) public function __construct(array $options = [])
{ {
$this->applyOptions($options); $this->applyOptions($options);
$this->getEnv(); $this->getEnv();
$localeManager = LocaleManager::getInstance(); $this->validateOptions();
$localeManager = new LocaleManager();
if (empty($this->genericFormats)) { if (empty($this->genericFormats)) {
// We don't put this in the class definition so it can be detected by xgettext. // We don't put this in the class definition so it can be detected by xgettext.
@ -185,7 +193,7 @@ class Config
* *
* @return string * @return string
*/ */
public static function addHttpToFormat($format) public static function addHttpToFormat(string $format)
{ {
$newFormat = []; $newFormat = [];
foreach (explode('/', $format) as $subformat) { foreach (explode('/', $format) as $subformat) {
@ -257,33 +265,17 @@ class Config
} }
} }
/**
* Get Config singleton instance.
*
* @return Config
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/** /**
* Set options from a YAML file. * Set options from a YAML file.
* *
* @param string $file Path to the YAML file * @param string $file Path to the YAML file
* @return void * @return Config
* @throws ConfigException * @throws ConfigException
*/ */
public static function setFile($file) public static function fromFile(string $file)
{ {
if (is_file($file)) { if (is_file($file)) {
$options = Yaml::parse(strval(file_get_contents($file))); return new self(Yaml::parse(strval(file_get_contents($file))));
self::$instance = new self($options);
self::$instance->validateOptions();
} else { } else {
throw new ConfigException("Can't find config file at " . $file); throw new ConfigException("Can't find config file at " . $file);
} }
@ -293,29 +285,13 @@ class Config
* Manually set some options. * Manually set some options.
* *
* @param mixed[] $options Options (see `config/config.example.yml` for available options) * @param mixed[] $options Options (see `config/config.example.yml` for available options)
* @param bool $update True to update an existing instance
* @return void * @return void
* @throws ConfigException * @throws ConfigException
*/ */
public static function setOptions(array $options, $update = true) public function setOptions(array $options)
{ {
if ($update) { $this->applyOptions($options);
$config = self::getInstance(); $this->validateOptions();
$config->applyOptions($options);
$config->validateOptions();
} else {
self::$instance = new self($options);
}
}
/**
* Destroy singleton instance.
*
* @return void
*/
public static function destroyInstance()
{
self::$instance = null;
} }
/** /**
@ -340,7 +316,7 @@ class Config
*/ */
public function getAppVersion() public function getAppVersion()
{ {
$version = PrettyVersions::getVersion(Versions::ROOT_PACKAGE_NAME); $version = PrettyVersions::getRootPackageVersion();
return $version->getPrettyVersion(); return $version->getPrettyVersion();
} }

41
classes/ConfigFactory.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace Alltube;
use Slim\Container;
use Symfony\Component\ErrorHandler\Debug;
/**
* Class ConfigFactory
* @package Alltube
*/
class ConfigFactory
{
/**
* @return Config
* @throws Exception\ConfigException
*/
public static function create(Container $container)
{
$configPath = __DIR__ . '/../config/config.yml';
if (is_file($configPath)) {
$config = Config::fromFile($configPath);
} else {
$config = new Config();
}
if ($config->uglyUrls) {
$container['router'] = new UglyRouter();
}
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();
}
return $config;
}
}

View File

@ -12,6 +12,7 @@ use Alltube\Library\Video;
use Alltube\LocaleManager; use Alltube\LocaleManager;
use Alltube\SessionManager; use Alltube\SessionManager;
use Aura\Session\Segment; use Aura\Session\Segment;
use Consolidation\Log\Logger;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
@ -70,6 +71,11 @@ abstract class BaseController
*/ */
protected $downloader; protected $downloader;
/**
* @var Logger
*/
protected $logger;
/** /**
* BaseController constructor. * BaseController constructor.
* *
@ -77,12 +83,14 @@ abstract class BaseController
*/ */
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
$this->config = Config::getInstance(); $this->config = $container->get('config');
$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'); $this->localeManager = $this->container->get('locale');
$this->downloader = $this->config->getDownloader(); $this->downloader = $this->config->getDownloader();
$this->logger = $this->container->get('logger');
$this->downloader->setLogger($this->logger);
if (!$this->config->stream) { if (!$this->config->stream) {
// Force HTTP if stream is not enabled. // Force HTTP if stream is not enabled.
@ -137,7 +145,7 @@ abstract class BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function displayError(Request $request, Response $response, $message) protected function displayError(Request $request, Response $response, string $message)
{ {
$controller = new FrontController($this->container); $controller = new FrontController($this->container);

View File

@ -21,6 +21,7 @@ use Alltube\Stream\PlaylistArchiveStream;
use Alltube\Stream\YoutubeStream; use Alltube\Stream\YoutubeStream;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
use Slim\Http\StatusCode;
use Slim\Http\Stream; use Slim\Http\Stream;
/** /**
@ -100,8 +101,12 @@ class DownloadController extends BaseController
*/ */
private function getConvertedAudioResponse(Request $request, Response $response) private function getConvertedAudioResponse(Request $request, Response $response)
{ {
$from = $request->getQueryParam('from'); $from = null;
$to = $request->getQueryParam('to'); $to = null;
if ($this->config->convertSeek) {
$from = $request->getQueryParam('from');
$to = $request->getQueryParam('to');
}
$response = $response->withHeader( $response = $response->withHeader(
'Content-Disposition', 'Content-Disposition',
@ -203,8 +208,8 @@ class DownloadController extends BaseController
$response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
$response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges')); $response = $response->withHeader('Accept-Ranges', $stream->getHeader('Accept-Ranges'));
$response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range')); $response = $response->withHeader('Content-Range', $stream->getHeader('Content-Range'));
if ($stream->getStatusCode() == 206) { if ($stream->getStatusCode() == StatusCode::HTTP_PARTIAL_CONTENT) {
$response = $response->withStatus(206); $response = $response->withStatus(StatusCode::HTTP_PARTIAL_CONTENT);
} }
if (isset($this->video->downloader_options->http_chunk_size)) { if (isset($this->video->downloader_options->http_chunk_size)) {

View File

@ -6,11 +6,13 @@
namespace Alltube\Controller; namespace Alltube\Controller;
use Alltube\CspMiddleware;
use Alltube\Library\Exception\PasswordException; use Alltube\Library\Exception\PasswordException;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Alltube\Library\Exception\WrongPasswordException; use Alltube\Library\Exception\WrongPasswordException;
use Alltube\Locale; use Alltube\Locale;
use Exception; use Exception;
use Slim\Http\StatusCode;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Throwable; use Throwable;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
@ -142,7 +144,7 @@ class FrontController extends BaseController
] ]
); );
return $response->withStatus(403); return $response->withStatus(StatusCode::HTTP_FORBIDDEN);
} }
/** /**
@ -241,7 +243,7 @@ class FrontController extends BaseController
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function displayError(Request $request, Response $response, $message) protected function displayError(Request $request, Response $response, string $message)
{ {
$this->view->render( $this->view->render(
$response, $response,
@ -256,7 +258,29 @@ class FrontController extends BaseController
] ]
); );
return $response->withStatus(500); return $response->withStatus(StatusCode::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* @param Request $request
* @param Response $response
* @return Response
*/
public function notFound(Request $request, Response $response)
{
return $this->displayError($request, $response, $this->localeManager->t('Page not found'))
->withStatus(StatusCode::HTTP_NOT_FOUND);
}
/**
* @param Request $request
* @param Response $response
* @return Response
*/
public function notAllowed(Request $request, Response $response)
{
return $this->displayError($request, $response, $this->localeManager->t('Method not allowed'))
->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED);
} }
/** /**
@ -270,6 +294,14 @@ class FrontController extends BaseController
*/ */
public function error(Request $request, Response $response, Throwable $error) public function error(Request $request, Response $response, Throwable $error)
{ {
$this->logger->error($error);
// We apply the CSP manually because middlewares are not called on error pages.
$cspMiddleware = new CspMiddleware($this->container);
/** @var Response $response */
$response = $cspMiddleware->applyHeader($response);
if ($this->config->debug) { if ($this->config->debug) {
$renderer = new HtmlErrorRenderer(true); $renderer = new HtmlErrorRenderer(true);
$exception = $renderer->render($error); $exception = $renderer->render($error);

View File

@ -9,6 +9,7 @@ namespace Alltube\Controller;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Response; use Slim\Http\Response;
use Slim\Http\StatusCode;
/** /**
* Controller that returns JSON. * Controller that returns JSON.
@ -38,7 +39,7 @@ class JsonController extends BaseController
return $response->withJson($this->video->getJson()); return $response->withJson($this->video->getJson());
} else { } else {
return $response->withJson(['error' => 'You need to provide the url parameter']) return $response->withJson(['error' => 'You need to provide the url parameter'])
->withStatus(400); ->withStatus(StatusCode::HTTP_BAD_REQUEST);
} }
} }
} }

65
classes/CspMiddleware.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace Alltube;
use ParagonIE\CSPBuilder\CSPBuilder;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\MessageInterface;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class CspMiddleware
* @package Alltube
*/
class CspMiddleware
{
/**
* @var Config
*/
private $config;
/**
* CspMiddleware constructor.
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->config = $container->get('config');
}
/**
* @param Response $response
* @return MessageInterface
*/
public function applyHeader(Response $response)
{
$csp = new CSPBuilder();
$csp->addDirective('default-src', [])
->addDirective('font-src', ['self' => true])
->addDirective('style-src', ['self' => true])
->addSource('img-src', '*');
if ($this->config->debug) {
// So symfony/debug and symfony/error-handler can work.
$csp->setDirective('script-src', ['unsafe-inline' => true])
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true]);
}
return $csp->injectCSPHeader($response);
}
/**
* @param Request $request
* @param Response $response
* @param callable $next
* @return mixed
*/
public function __invoke(Request $request, Response $response, callable $next)
{
$response = $this->applyHeader($response);
return $next($request, $response);
}
}

36
classes/ErrorHandler.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace Alltube;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Throwable;
/**
* Class ErrorHandler
* @package Alltube
*/
class ErrorHandler
{
/**
* Last resort if the error has not been caught by the Slim error handler for some reason.
* @param Throwable $e
* @return void
*/
public static function handle(Throwable $e)
{
error_log($e);
if (class_exists(HtmlErrorRenderer::class)) {
// If dev dependencies are loaded, we can use symfony/error-handler.
$renderer = new HtmlErrorRenderer(true);
$exception = $renderer->render($e);
http_response_code($exception->getStatusCode());
die($exception->getAsString());
} else {
http_response_code(500);
die('Error when starting the app: ' . htmlentities($e->getMessage()));
}
}
}

View File

@ -4,6 +4,10 @@ namespace Alltube\Exception;
use Exception; use Exception;
/**
* Class ConfigException
* @package Alltube\Exception
*/
class ConfigException extends Exception class ConfigException extends Exception
{ {

View File

@ -0,0 +1,14 @@
<?php
namespace Alltube\Exception;
use Exception;
/**
* Class DependencyException
* @package Alltube\Exception
*/
class DependencyException extends Exception
{
}

View File

@ -34,7 +34,7 @@ class Locale
* *
* @param string $locale ISO 15897 code * @param string $locale ISO 15897 code
*/ */
public function __construct($locale) public function __construct(string $locale)
{ {
$parse = AcceptLanguage::parse($locale); $parse = AcceptLanguage::parse($locale);
$this->language = $parse[1]['language']; $this->language = $parse[1]['language'];

View File

@ -50,17 +50,10 @@ class LocaleManager
*/ */
private $translator; private $translator;
/**
* Singleton instance.
*
* @var LocaleManager|null
*/
private static $instance;
/** /**
* LocaleManager constructor. * LocaleManager constructor.
*/ */
private function __construct() public function __construct()
{ {
$session = SessionManager::getSession(); $session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class); $this->sessionSegment = $session->getSegment(self::class);
@ -142,11 +135,11 @@ class LocaleManager
* Smarty "t" block. * Smarty "t" block.
* *
* @param mixed[] $params Block parameters * @param mixed[] $params Block parameters
* @param string $text Block content * @param string|null $text Block content
* *
* @return string Translated string * @return string Translated string
*/ */
public function smartyTranslate(array $params, $text) public function smartyTranslate(array $params, string $text = null)
{ {
if (isset($params['params'])) { if (isset($params['params'])) {
return $this->t($text, $params['params']); return $this->t($text, $params['params']);
@ -158,37 +151,17 @@ class LocaleManager
/** /**
* Translate a string. * Translate a string.
* *
* @param string $string String to translate * @param string|null $string $string String to translate
* *
* @param mixed[] $params * @param mixed[] $params
* @return string Translated string * @return string Translated string
*/ */
public function t($string, array $params = []) public function t(string $string = null, array $params = [])
{ {
return $this->translator->trans($string, $params); if (isset($string)) {
} 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; return '';
}
/**
* Destroy singleton instance.
*
* @return void
*/
public static function destroyInstance()
{
self::$instance = null;
} }
} }

View File

@ -0,0 +1,26 @@
<?php
namespace Alltube;
use Alltube\Exception\DependencyException;
/**
* Class LocaleManagerFactory
* @package Alltube
*/
class LocaleManagerFactory
{
/**
* @return LocaleManager|null
* @throws DependencyException
*/
public static function create()
{
if (!class_exists('Locale')) {
throw new DependencyException('You need to install the intl extension for PHP.');
}
return new LocaleManager();
}
}

35
classes/LoggerFactory.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace Alltube;
use Consolidation\Log\Logger;
use Consolidation\Log\LogOutputStyler;
use Slim\Container;
use Symfony\Component\Console\Output\ConsoleOutput;
/**
* Class LoggerFactory
* @package Alltube
*/
class LoggerFactory
{
/**
* @param Container $container
* @return Logger
*/
public static function create(Container $container)
{
$config = $container->get('config');
if ($config->debug) {
$verbosity = ConsoleOutput::VERBOSITY_DEBUG;
} else {
$verbosity = ConsoleOutput::VERBOSITY_NORMAL;
}
$logger = new Logger(new ConsoleOutput($verbosity));
$logger->setLogOutputStyler(new LogOutputStyler());
return $logger;
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Alltube\Robo\Plugin\Commands;
use Robo\Task\Archive\Pack;
use Robo\Task\Base\Exec;
use Robo\Task\Composer\Install;
use Robo\Task\Filesystem\FilesystemStack;
use Robo\Task\Vcs\GitStack;
use Robo\Tasks;
/**
* Manage robo tasks.
*/
class ReleaseCommand extends Tasks
{
/**
* Create release archive
* @return void
*/
public function release()
{
$this->stopOnFail();
/** @var Exec $gitTask */
$gitTask = $this->taskExec('git');
$result = $gitTask
->arg('describe')
->run();
$result->provideOutputdata();
$tmpDir = $this->_tmpDir();
$filename = 'alltube-' . trim((string)$result->getOutputData()) . '.zip';
/** @var FilesystemStack $rmTask */
$rmTask = $this->taskFilesystemStack();
$rmTask->remove($filename)
->run();
/** @var GitStack $gitTask */
$gitTask = $this->taskGitStack();
$gitTask->cloneRepo(__DIR__ . '/../../../../', $tmpDir)
->run();
/** @var Install $composerTask */
$composerTask = $this->taskComposerInstall();
$composerTask->dir($tmpDir)
->optimizeAutoloader()
->noDev()
->run();
/** @var Pack $packTask */
$packTask = $this->taskPack($filename);
$packTask->addDir('alltube', $tmpDir)
->run();
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Alltube;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Router;
/**
* Class RouterPathMiddleware
* @package Alltube
*/
class RouterPathMiddleware
{
/**
* @var Router
*/
private $router;
/**
* RouterPathMiddleware constructor.
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->router = $container->get('router');
}
/**
* @param Request $request
* @param Response $response
* @param callable $next
* @return mixed
*/
public function __invoke(Request $request, Response $response, callable $next)
{
if ($path = current($request->getHeader('X-Forwarded-Path'))) {
$this->router->setBasePath($path);
}
return $next($request, $response);
}
}

View File

@ -79,7 +79,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Add data to the archive. * Add data to the archive.
* *
* @param string $data Data * @param mixed $data Data
* *
* @return void * @return void
*/ */
@ -99,7 +99,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Write data to the stream. * Write data to the stream.
* *
* @param string $string The string that is to be written * @param mixed $string The string that is to be written
* *
* @return int|false * @return int|false
*/ */
@ -171,7 +171,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Get stream metadata as an associative array or retrieve a specific key. * Get stream metadata as an associative array or retrieve a specific key.
* *
* @param string $key string $key Specific metadata to retrieve. * @param string|null $key string $key Specific metadata to retrieve.
* *
* @return array|mixed|null * @return array|mixed|null
*/ */
@ -228,7 +228,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Seek to a position in the stream. * Seek to a position in the stream.
* *
* @param int $offset Offset * @param mixed $offset Offset
* @param int $whence Specifies how the cursor position will be calculated * @param int $whence Specifies how the cursor position will be calculated
* *
* @return void * @return void
@ -272,7 +272,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/** /**
* Read data from the stream. * Read data from the stream.
* *
* @param int $count Number of bytes to read * @param mixed $count Number of bytes to read
* *
* @return string|false * @return string|false
* @throws AlltubeLibraryException * @throws AlltubeLibraryException

View File

@ -35,7 +35,7 @@ class YoutubeChunkStream implements StreamInterface
/** /**
* Read data from the stream. * Read data from the stream.
* *
* @param int $length Read up to $length bytes from the object and return * @param mixed $length Read up to $length bytes from the object and return
* *
* @return string * @return string
*/ */
@ -121,7 +121,7 @@ class YoutubeChunkStream implements StreamInterface
/** /**
* Seek to a position in the stream. * Seek to a position in the stream.
* *
* @param int $offset Stream offset * @param mixed $offset Stream offset
* @param int $whence Specifies how the cursor position will be calculated * @param int $whence Specifies how the cursor position will be calculated
* *
* @return void * @return void
@ -154,7 +154,7 @@ class YoutubeChunkStream implements StreamInterface
/** /**
* Write data to the stream. * Write data to the stream.
* *
* @param string $string The string that is to be written * @param mixed $string The string that is to be written
* *
* @return mixed * @return mixed
*/ */
@ -186,7 +186,7 @@ class YoutubeChunkStream implements StreamInterface
/** /**
* Get stream metadata as an associative array or retrieve a specific key. * Get stream metadata as an associative array or retrieve a specific key.
* *
* @param string $key Specific metadata to retrieve. * @param string|null $key Specific metadata to retrieve.
* *
* @return array|mixed|null * @return array|mixed|null
*/ */

View File

@ -42,7 +42,7 @@ class UglyRouter extends Router
/** /**
* Build the path for a named route including the base path. * Build the path for a named route including the base path.
* *
* @param string $name Route name * @param mixed $name Route name
* @param string[] $data Named argument replacement data * @param string[] $data Named argument replacement data
* @param string[] $queryParams Optional query string parameters * @param string[] $queryParams Optional query string parameters
* *

View File

@ -8,6 +8,7 @@ namespace Alltube;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Slim\Http\Request; use Slim\Http\Request;
use Slim\Http\Uri;
use Slim\Views\Smarty; use Slim\Views\Smarty;
use Slim\Views\SmartyPlugins; use Slim\Views\SmartyPlugins;
use SmartyException; use SmartyException;
@ -21,7 +22,7 @@ class ViewFactory
* Create Smarty view object. * Create Smarty view object.
* *
* @param ContainerInterface $container Slim dependency container * @param ContainerInterface $container Slim dependency container
* @param Request $request PSR-7 request * @param Request|null $request PSR-7 request
* *
* @return Smarty * @return Smarty
* @throws SmartyException * @throws SmartyException
@ -33,14 +34,30 @@ class ViewFactory
} }
$view = new Smarty(__DIR__ . '/../templates/'); $view = new Smarty(__DIR__ . '/../templates/');
/** @var Uri $uri */
$uri = $request->getUri();
if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) { if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) {
$request = $request->withUri($request->getUri()->withScheme('https')->withPort(443)); $uri = $uri->withScheme('https')->withPort(443);
}
// set values from X-Forwarded-* headers
if ($host = current($request->getHeader('X-Forwarded-Host'))) {
$uri = $uri->withHost($host);
}
if ($port = current($request->getHeader('X-Forwarded-Port'))) {
$uri = $uri->withPort(intVal($port));
}
if ($path = current($request->getHeader('X-Forwarded-Path'))) {
$uri = $uri->withBasePath($path);
} }
/** @var LocaleManager $localeManager */ /** @var LocaleManager $localeManager */
$localeManager = $container->get('locale'); $localeManager = $container->get('locale');
$smartyPlugins = new SmartyPlugins($container->get('router'), $request->getUri()->withUserInfo(null)); $smartyPlugins = new SmartyPlugins($container->get('router'), $uri->withUserInfo(''));
$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']); $view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);

View File

@ -1,55 +1,9 @@
{ {
"name": "rudloff/alltube", "name": "rudloff/alltube",
"description": "HTML GUI for youtube-dl",
"license": "GPL-3.0-only",
"homepage": "http://alltubedownload.net/",
"type": "project", "type": "project",
"require": { "description": "HTML GUI for youtube-dl",
"ext-intl": "*", "homepage": "http://alltubedownload.net/",
"ext-json": "*", "license": "GPL-3.0-only",
"aura/session": "^2.1",
"barracudanetworks/archivestream-php": "^1.0",
"jawira/case-converter": "^3.4",
"jean85/pretty-package-versions": "^1.3",
"mathmarques/smarty-view": "^1.1",
"npm-asset/open-sans-fontface": "^1.4",
"rinvex/countries": "^6.1",
"rudloff/alltube-library": "^0.1.0",
"symfony/finder": "^5.0",
"symfony/translation": "^4.0",
"symfony/yaml": "^4.0",
"ytdl-org/youtube-dl": "^2020.06",
"zonuexe/http-accept-language": "^0.4.1"
},
"require-dev": {
"consolidation/robo": "^2.1",
"php-mock/php-mock-mockery": "^1.3",
"phpro/grumphp": "^0.18.0",
"phpstan/phpstan": "^0.12.25",
"phpunit/phpunit": "^8.4",
"roave/security-advisories": "dev-master",
"smarty-gettext/smarty-gettext": "^1.6",
"squizlabs/php_codesniffer": "^3.5",
"symfony/error-handler": "^5.0",
"symfony/var-dumper": "^5.0"
},
"repositories": [
{
"type": "composer",
"url": "https://asset-packagist.org"
},
{
"type": "package",
"package": {
"name": "ytdl-org/youtube-dl",
"version": "2020.06.16.1",
"dist": {
"type": "zip",
"url": "https://github.com/ytdl-org/youtube-dl/archive/2020.06.16.1.zip"
}
}
}
],
"authors": [ "authors": [
{ {
"name": "Pierre Rudloff", "name": "Pierre Rudloff",
@ -64,29 +18,79 @@
"role": "Designer" "role": "Designer"
} }
], ],
"require": {
"ext-intl": "*",
"ext-json": "*",
"aura/session": "^2.1",
"barracudanetworks/archivestream-php": "^1.0",
"consolidation/log": "^2.0",
"jawira/case-converter": "^3.4",
"jean85/pretty-package-versions": "^1.3",
"mathmarques/smarty-view": "^1.1",
"paragonie/csp-builder": "^2.5",
"rinvex/countries": "^6.1",
"rudloff/alltube-library": "dev-develop",
"symfony/finder": "^5.0",
"symfony/translation": "^4.0",
"symfony/yaml": "^4.0",
"webfontkit/open-sans": "^1.0",
"ytdl-org/youtube-dl": "^2020.09",
"zonuexe/http-accept-language": "^0.4.1"
},
"require-dev": {
"consolidation/robo": "^2.1",
"ergebnis/composer-normalize": "^2.6",
"insite/composer-dangling-locked-deps": "^0.2.0",
"php-mock/php-mock-mockery": "^1.3",
"phpro/grumphp": "^1.0",
"phpstan/phpstan": "^0.12.25",
"phpunit/phpunit": "^8.4",
"sensiolabs/security-checker": "^6.0",
"smarty-gettext/smarty-gettext": "^1.6",
"squizlabs/php_codesniffer": "^3.5",
"symfony/error-handler": "^5.0",
"symfony/var-dumper": "^5.0"
},
"config": {
"platform": {
"php": "7.3.11"
},
"sort-packages": true
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Alltube\\": "classes/", "Alltube\\": "classes/"
"Alltube\\Stream\\": "classes/streams/", }
"Alltube\\Exception\\": "classes/exceptions/", },
"Alltube\\Controller\\": "controllers/", "autoload-dev": {
"psr-4": {
"Alltube\\Test\\": "tests/" "Alltube\\Test\\": "tests/"
} }
}, },
"repositories": [
{
"type": "package",
"package": {
"name": "ytdl-org/youtube-dl",
"version": "2020.09.20",
"dist": {
"type": "zip",
"url": "https://github.com/ytdl-org/youtube-dl/archive/2020.09.20.zip"
}
}
}
],
"scripts": { "scripts": {
"lint": "grumphp run --ansi", "lint": "grumphp run --ansi",
"test": "phpunit",
"release": "robo release --ansi", "release": "robo release --ansi",
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
],
"update-locales": [ "update-locales": [
"tsmarty2c.php templates > i18n/template.pot", "tsmarty2c.php templates > i18n/template.pot",
"xgettext --omit-header -kt -j -o i18n/template.pot classes/*.php classes/*/*.php controllers/*" "xgettext --omit-header -kt -j -o i18n/template.pot classes/*.php classes/*/*.php"
], ],
"youtube-dl": "vendor/ytdl-org/youtube-dl/youtube_dl/__main__.py" "youtube-dl": "vendor/ytdl-org/youtube-dl/youtube_dl/__main__.py"
},
"config": {
"sort-packages": true,
"platform": {
"php": "7.3.11"
}
} }
} }

3392
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -55,3 +55,9 @@ genericFormats:
# Enable debug mode. # Enable debug mode.
debug: false debug: false
# True to enable audio conversion mode by default
defaultAudio: false
# False to disable convert seek functionality
convertSeek: true

View File

@ -1,20 +0,0 @@
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
src: local('Open Sans Light'), local('OpenSans-Light'), url(../vendor/npm-asset/open-sans-fontface/fonts/Light/OpenSans-Regular.ttf);
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans'), local('OpenSans'), url(../vendor/npm-asset/open-sans-fontface/fonts/Regular/OpenSans-Regular.ttf);
}
.small-font {
font-size: 13px;
}
.large-font {
font-size:24px;
}

View File

@ -1,11 +1,22 @@
body { body {
background-color: #ebebeb; background-color: #ebebeb;
background-image: url("../img/fond.jpg"); background-image: url("../img/fond.jpg");
}
.page {
font-family: "Open Sans", sans-serif; font-family: "Open Sans", sans-serif;
font-weight: 400; font-weight: 400;
text-align: center; text-align: center;
} }
.small-font {
font-size: 13px;
}
.large-font {
font-size:24px;
}
/* Header */ /* Header */
header { header {

View File

@ -1,16 +1,19 @@
--- ---
parameters: grumphp:
ascii: ~ extensions:
- ComposerDanglingLockedDeps\GrumPHP\Loader
ascii:
succeeded: ~
failed: ~
tasks: tasks:
jsonlint: ~ jsonlint: ~
xmllint: ~ xmllint: ~
yamllint: ~ yamllint: ~
composer: ~ composer: ~
securitychecker: ~
composer_normalize: ~
composer_dangling_locked_deps: ~
phpcs: phpcs:
standard: PSR12 standard: PSR12
ignore_patterns:
- RoboFile.php
phpstan: phpstan:
level: max level: max
ignore_patterns:
- RoboFile.php

5
heroku.yml Normal file
View File

@ -0,0 +1,5 @@
build:
docker:
web: Dockerfile
run:
web: bash /var/www/html/resources/heroku-docker-start.sh

View File

@ -1,6 +1,27 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n" msgstr "Content-Type: text/plain; charset=UTF-8\n"
#: templates/playlist.tpl:12
msgid "Videos extracted from @title:"
msgstr ""
#: templates/playlist.tpl:38 templates/password.tpl:11 templates/index.tpl:19
#: templates/info.tpl:101
msgid "Download"
msgstr ""
#: templates/playlist.tpl:39
msgid "More options"
msgstr ""
#: templates/inc/header.tpl:4
msgid "Switch language"
msgstr ""
#: templates/inc/header.tpl:8
msgid "Set language"
msgstr ""
#: templates/inc/footer.tpl:8 #: templates/inc/footer.tpl:8
msgid "Code by @dev" msgid "Code by @dev"
msgstr "" msgstr ""
@ -25,12 +46,44 @@ msgstr ""
msgid "Donate" msgid "Donate"
msgstr "" msgstr ""
#: templates/inc/header.tpl:4 #: templates/password.tpl:5
msgid "Switch language" msgid "This video is protected"
msgstr "" msgstr ""
#: templates/inc/header.tpl:8 #: templates/password.tpl:6
msgid "Set language" 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 ""
#: templates/index.tpl:25
msgid "Audio only (MP3)"
msgstr ""
#: templates/index.tpl:28
msgid "From"
msgstr ""
#: templates/index.tpl:31
msgid "to"
msgstr ""
#: templates/index.tpl:39
msgid "See all supported websites"
msgstr ""
#: templates/index.tpl:41
msgid "Drag this to your bookmarks bar:"
msgstr ""
#: templates/index.tpl:43
msgid "Bookmarklet"
msgstr "" msgstr ""
#: templates/info.tpl:11 #: templates/info.tpl:11
@ -81,23 +134,6 @@ msgstr ""
msgid "kbit/s audio" msgid "kbit/s audio"
msgstr "" msgstr ""
#: templates/info.tpl:101 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 ""
#: templates/playlist.tpl:39
msgid "More options"
msgstr ""
#: templates/extractors.tpl:4 controllers/FrontController.php:109
msgid "Supported websites"
msgstr ""
#: templates/error.tpl:5 #: templates/error.tpl:5
msgid "An error occurred" msgid "An error occurred"
msgstr "" msgstr ""
@ -106,106 +142,71 @@ msgstr ""
msgid "Please check the URL of your video." msgid "Please check the URL of your video."
msgstr "" msgstr ""
#: templates/password.tpl:5 #: templates/extractors.tpl:4 classes/Controller/FrontController.php:109
msgid "This video is protected" msgid "Supported websites"
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 ""
#: templates/index.tpl:25
msgid "Audio only (MP3)"
msgstr ""
#: templates/index.tpl:28
msgid "From"
msgstr ""
#: templates/index.tpl:31
msgid "to"
msgstr ""
#: templates/index.tpl:39
msgid "See all supported websites"
msgstr ""
#: templates/index.tpl:41
msgid "Drag this to your bookmarks bar:"
msgstr ""
#: templates/index.tpl:43
msgid "Bookmarklet"
msgstr ""
#: classes/Config.php:156
msgid "Best"
msgstr ""
#: classes/Config.php:157
msgid "Remux best video with best audio"
msgstr "" msgstr ""
#: classes/Config.php:158 #: 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" msgid "Worst"
msgstr "" msgstr ""
#: controllers/DownloadController.php:63 controllers/FrontController.php:164 #: classes/Controller/DownloadController.php:63
#: classes/Controller/FrontController.php:164
msgid "Wrong password" msgid "Wrong password"
msgstr "" msgstr ""
#: controllers/DownloadController.php:68 #: classes/Controller/DownloadController.php:68
msgid "Conversion of playlists is not supported." msgid "Conversion of playlists is not supported."
msgstr "" msgstr ""
#: controllers/DownloadController.php:75 #: classes/Controller/DownloadController.php:75
msgid "Conversion of M3U8 files is not supported." msgid "Conversion of M3U8 files is not supported."
msgstr "" msgstr ""
#: controllers/DownloadController.php:81 #: classes/Controller/DownloadController.php:81
msgid "Conversion of DASH segments is not supported." msgid "Conversion of DASH segments is not supported."
msgstr "" msgstr ""
#: controllers/FrontController.php:63 #: classes/Controller/FrontController.php:63
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:110 #: classes/Controller/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:136 #: classes/Controller/FrontController.php:136
msgid "Password prompt" msgid "Password prompt"
msgstr "" msgstr ""
#: controllers/FrontController.php:138 #: classes/Controller/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:172 #: classes/Controller/FrontController.php:172
msgid "Video download" msgid "Video download"
msgstr "" msgstr ""
#: controllers/FrontController.php:174 #: classes/Controller/FrontController.php:174
msgid "Download video from @extractor" msgid "Download video from @extractor"
msgstr "" msgstr ""
#: controllers/FrontController.php:180 #: classes/Controller/FrontController.php:180
msgid "Download @title from @extractor" msgid "Download @title from @extractor"
msgstr "" msgstr ""
#: controllers/FrontController.php:253 #: classes/Controller/FrontController.php:253
msgid "Error" msgid "Error"
msgstr "" msgstr ""

150
index.php
View File

@ -2,115 +2,97 @@
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
use Alltube\Config; use Alltube\ConfigFactory;
use Alltube\Controller\DownloadController; use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController; use Alltube\Controller\FrontController;
use Alltube\Controller\JsonController; use Alltube\Controller\JsonController;
use Alltube\LocaleManager; use Alltube\CspMiddleware;
use Alltube\ErrorHandler;
use Alltube\LocaleManagerFactory;
use Alltube\LocaleMiddleware; use Alltube\LocaleMiddleware;
use Alltube\UglyRouter; use Alltube\LoggerFactory;
use Alltube\RouterPathMiddleware;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Slim\App; use Slim\App;
use Slim\Container; use Slim\Container;
use Symfony\Component\ErrorHandler\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']));
die; die;
} }
if (is_file(__DIR__ . '/config/config.yml')) {
try {
Config::setFile(__DIR__ . '/config/config.yml');
} catch (Exception $e) {
die('Could not load config file: ' . $e->getMessage());
}
}
// Create app.
$app = new App();
/** @var Container $container */
$container = $app->getContainer();
// Load config.
$config = Config::getInstance();
if ($config->uglyUrls) {
$container['router'] = new UglyRouter();
}
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')) {
die('You need to install the intl extension for PHP.');
}
$container['locale'] = LocaleManager::getInstance();
$app->add(new LocaleMiddleware($container));
// Smarty.
try { try {
// Create app.
$app = new App();
/** @var Container $container */
$container = $app->getContainer();
// Config.
$container['config'] = ConfigFactory::create($container);
// Locales.
$container['locale'] = LocaleManagerFactory::create();
// Smarty.
$container['view'] = ViewFactory::create($container); $container['view'] = ViewFactory::create($container);
} catch (SmartyException $e) {
die('Could not load Smarty: ' . $e->getMessage());
}
// Controllers. // Logger.
$frontController = new FrontController($container); $container['logger'] = LoggerFactory::create($container);
$jsonController = new JsonController($container);
$downloadController = new DownloadController($container);
// Error handling. // Middlewares.
$container['errorHandler'] = [$frontController, 'error']; $app->add(new LocaleMiddleware($container));
$container['phpErrorHandler'] = [$frontController, 'error']; $app->add(new RouterPathMiddleware($container));
$app->add(new CspMiddleware($container));
// Routes. // Controllers.
$app->get( $frontController = new FrontController($container);
'/', $jsonController = new JsonController($container);
[$frontController, 'index'] $downloadController = new DownloadController($container);
)->setName('index');
$app->get( // Error handling.
'/extractors', $container['errorHandler'] = [$frontController, 'error'];
[$frontController, 'extractors'] $container['phpErrorHandler'] = [$frontController, 'error'];
)->setName('extractors'); $container['notFoundHandler'] = [$frontController, 'notFound'];
$container['notAllowedHandler'] = [$frontController, 'notAllowed'];
$app->any( // Routes.
'/info', $app->get(
[$frontController, 'info'] '/',
)->setName('info'); [$frontController, 'index']
)->setName('index');
$app->any( $app->get(
'/watch', '/extractors',
[$frontController, 'info'] [$frontController, 'extractors']
); )->setName('extractors');
$app->any( $app->any(
'/download', '/info',
[$downloadController, 'download'] [$frontController, 'info']
)->setName('download'); )->setName('info');
$app->get( $app->any(
'/locale/{locale}', '/watch',
[$frontController, 'locale'] [$frontController, 'info']
)->setName('locale'); );
$app->get( $app->any(
'/json', '/download',
[$jsonController, 'json'] [$downloadController, 'download']
)->setName('json'); )->setName('download');
$app->get(
'/locale/{locale}',
[$frontController, 'locale']
)->setName('locale');
$app->get(
'/json',
[$jsonController, 'json']
)->setName('json');
try {
$app->run(); $app->run();
} catch (SmartyException $e) {
die('Smarty could not compile the template file: ' . $e->getMessage());
} catch (Throwable $e) { } catch (Throwable $e) {
// Last resort if the error has not been caught by the error handler for some reason. ErrorHandler::handle($e);
die('Error when starting the app: ' . htmlentities($e->getMessage()));
} }

View File

@ -3,7 +3,6 @@
<filter> <filter>
<whitelist> <whitelist>
<directory>classes/</directory> <directory>classes/</directory>
<directory>controllers/</directory>
</whitelist> </whitelist>
</filter> </filter>
<testsuites> <testsuites>

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
a2dismod mpm_event
sed -i "s/Listen 80/Listen ${PORT:-80}/g" /etc/apache2/ports.conf
apache2-foreground "$@"

View File

@ -1 +1 @@
python-2.7.17 python-3.8.6

View File

@ -3,7 +3,6 @@
<main class="main error"> <main class="main error">
{include file="inc/logo.tpl"} {include file="inc/logo.tpl"}
<h2>{t}An error occurred{/t}</h2> <h2>{t}An error occurred{/t}</h2>
{t}Please check the URL of your video.{/t}
<p><i>{$error|escape|nl2br}</i></p> <p><i>{$error|escape|nl2br}</i></p>
</main> </main>
{include file='inc/footer.tpl'} {include file='inc/footer.tpl'}

View File

@ -36,5 +36,6 @@
</a> </a>
</div> </div>
</footer> </footer>
</div>
</body> </body>
</html> </html>

View File

@ -8,7 +8,7 @@
<meta name="twitter:description" content="{$description|escape}"/> <meta name="twitter:description" content="{$description|escape}"/>
<meta property="og:description" content="{$description|escape}"/> <meta property="og:description" content="{$description|escape}"/>
{/if} {/if}
<link rel="stylesheet" href="{base_url}/css/fonts.css"/> <link rel="stylesheet" href="{base_url}/vendor/webfontkit/open-sans/open-sans.css"/>
<link rel="stylesheet" href="{base_url}/css/style.css"/> <link rel="stylesheet" href="{base_url}/css/style.css"/>
<title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title> <title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title>
<link rel="canonical" href="{$canonical}"/> <link rel="canonical" href="{$canonical}"/>
@ -23,4 +23,5 @@
<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()})"/>
</head> </head>
<body class="{$class}"> <body>
<div class="page {$class}">

View File

@ -20,10 +20,11 @@
{if $config->convert} {if $config->convert}
<div class="mp3 small-font"> <div class="mp3 small-font">
<div class="mp3-inner"> <div class="mp3-inner">
<input type="checkbox" id="audio" class="audio" name="audio"> <input type="checkbox" id="audio" class="audio" name="audio" {($config->defaultAudio) ? 'checked' : ''}>
<label for="audio"><span class="ui"></span> <label for="audio"><span class="ui"></span>
{t}Audio only (MP3){/t} {t}Audio only (MP3){/t}
</label> </label>
{if $config->convertSeek}
<div class="seekOptions"> <div class="seekOptions">
<label for="from">{t}From{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?" <label for="from">{t}From{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
placeholder="HH:MM:SS" value="" name="from" placeholder="HH:MM:SS" value="" name="from"
@ -31,6 +32,7 @@
<label for="to">{t}to{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?" <label for="to">{t}to{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
placeholder="HH:MM:SS" value="" name="to" id="to"/> placeholder="HH:MM:SS" value="" name="to" id="to"/>
</div> </div>
{/if}
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -6,8 +6,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/** /**
@ -27,22 +25,12 @@ abstract class BaseTest extends TestCase
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
Config::setFile($this->getConfigFile());
$this->checkRequirements(); $this->checkRequirements();
} }
/**
* Destroy properties after test.
*/
protected function tearDown(): void
{
Config::destroyInstance();
}
/** /**
* Check tests requirements. * Check tests requirements.
* @return void * @return void
@ -53,10 +41,10 @@ abstract class BaseTest extends TestCase
$requires = []; $requires = [];
if (isset($annotations['class']['requires'])) { if (isset($annotations['class']['requires'])) {
$requires += $annotations['class']['requires']; $requires = array_merge($requires, $annotations['class']['requires']);
} }
if (isset($annotations['method']['requires'])) { if (isset($annotations['method']['requires'])) {
$requires += $annotations['method']['requires']; $requires = array_merge($requires, $annotations['method']['requires']);
} }
foreach ($requires as $require) { foreach ($requires as $require) {

View File

@ -14,23 +14,6 @@ use Alltube\Exception\ConfigException;
*/ */
class ConfigTest extends BaseTest class ConfigTest extends BaseTest
{ {
/**
* Config class instance.
*
* @var Config
*/
private $config;
/**
* Prepare tests.
* @throws ConfigException
*/
protected function setUp(): void
{
parent::setUp();
$this->config = Config::getInstance();
}
/** /**
* Test the getInstance function. * Test the getInstance function.
@ -39,21 +22,7 @@ class ConfigTest extends BaseTest
*/ */
public function testGetInstance() public function testGetInstance()
{ {
$config = Config::getInstance(); $config = new Config();
$this->assertEquals(false, $config->convert);
$this->assertConfig($config);
}
/**
* Test the getInstance function.
*
* @return void
*/
public function testGetInstanceFromScratch()
{
Config::destroyInstance();
$config = Config::getInstance();
$this->assertEquals(false, $config->convert); $this->assertEquals(false, $config->convert);
$this->assertConfig($config); $this->assertConfig($config);
} }
@ -75,6 +44,8 @@ class ConfigTest extends BaseTest
$this->assertIsBool($config->uglyUrls); $this->assertIsBool($config->uglyUrls);
$this->assertIsBool($config->stream); $this->assertIsBool($config->stream);
$this->assertIsBool($config->remux); $this->assertIsBool($config->remux);
$this->assertIsBool($config->defaultAudio);
$this->assertIsBool($config->convertSeek);
$this->assertIsInt($config->audioBitrate); $this->assertIsInt($config->audioBitrate);
} }
@ -86,8 +57,8 @@ class ConfigTest extends BaseTest
*/ */
public function testSetFile() public function testSetFile()
{ {
Config::setFile($this->getConfigFile()); $config = Config::fromFile($this->getConfigFile());
$this->assertConfig($this->config); $this->assertConfig($config);
} }
/** /**
@ -98,7 +69,7 @@ class ConfigTest extends BaseTest
public function testSetFileWithMissingFile() public function testSetFileWithMissingFile()
{ {
$this->expectException(ConfigException::class); $this->expectException(ConfigException::class);
Config::setFile('foo'); Config::fromFile('foo');
} }
/** /**
@ -109,21 +80,8 @@ class ConfigTest extends BaseTest
*/ */
public function testSetOptions() public function testSetOptions()
{ {
Config::setOptions(['appName' => 'foo']); $config = new Config();
$config = Config::getInstance(); $config->setOptions(['appName' => 'foo']);
$this->assertEquals('foo', $config->appName);
}
/**
* Test the setOptions function.
*
* @return void
* @throws ConfigException
*/
public function testSetOptionsWithoutUpdate()
{
Config::setOptions(['appName' => 'foo'], false);
$config = Config::getInstance();
$this->assertEquals('foo', $config->appName); $this->assertEquals('foo', $config->appName);
} }
@ -135,7 +93,8 @@ class ConfigTest extends BaseTest
public function testSetOptionsWithBadYoutubedl() public function testSetOptionsWithBadYoutubedl()
{ {
$this->expectException(ConfigException::class); $this->expectException(ConfigException::class);
Config::setOptions(['youtubedl' => 'foo']); $config = new Config();
$config->setOptions(['youtubedl' => 'foo']);
} }
/** /**
@ -146,7 +105,8 @@ class ConfigTest extends BaseTest
public function testSetOptionsWithBadPython() public function testSetOptionsWithBadPython()
{ {
$this->expectException(ConfigException::class); $this->expectException(ConfigException::class);
Config::setOptions(['python' => 'foo']); $config = new Config();
$config->setOptions(['python' => 'foo']);
} }
/** /**
@ -157,10 +117,8 @@ class ConfigTest extends BaseTest
*/ */
public function testGetInstanceWithEnv() public function testGetInstanceWithEnv()
{ {
Config::destroyInstance();
putenv('CONVERT=1'); putenv('CONVERT=1');
Config::setFile($this->getConfigFile()); $config = Config::fromFile($this->getConfigFile());
$config = Config::getInstance();
$this->assertEquals(true, $config->convert); $this->assertEquals(true, $config->convert);
putenv('CONVERT'); putenv('CONVERT');
} }

View File

@ -6,12 +6,14 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Controller\BaseController; use Alltube\Controller\BaseController;
use Alltube\Controller\DownloadController; use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController; use Alltube\Controller\FrontController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\LocaleManager; use Alltube\LocaleManager;
use Alltube\ViewFactory; use Alltube\ViewFactory;
use Psr\Log\NullLogger;
use Slim\Container; use Slim\Container;
use Slim\Http\Environment; use Slim\Http\Environment;
use Slim\Http\Request; use Slim\Http\Request;
@ -61,8 +63,10 @@ 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['config'] = Config::fromFile($this->getConfigFile());
$this->container['locale'] = new LocaleManager();
$this->container['view'] = ViewFactory::create($this->container, $this->request); $this->container['view'] = ViewFactory::create($this->container, $this->request);
$this->container['logger'] = new NullLogger();
$frontController = new FrontController($this->container); $frontController = new FrontController($this->container);
$downloadController = new DownloadController($this->container); $downloadController = new DownloadController($this->container);
@ -87,7 +91,7 @@ abstract class ControllerTest extends BaseTest
* *
* @return Response HTTP response * @return Response HTTP response
*/ */
protected function getRequestResult($request, array $params) protected function getRequestResult(string $request, array $params)
{ {
return $this->controller->$request( return $this->controller->$request(
$this->request->withQueryParams($params), $this->request->withQueryParams($params),
@ -103,7 +107,7 @@ abstract class ControllerTest extends BaseTest
* *
* @return void * @return void
*/ */
protected function assertRequestIsOk($request, array $params = []) protected function assertRequestIsOk(string $request, array $params = [])
{ {
$this->assertTrue($this->getRequestResult($request, $params)->isOk()); $this->assertTrue($this->getRequestResult($request, $params)->isOk());
} }
@ -116,7 +120,7 @@ abstract class ControllerTest extends BaseTest
* *
* @return void * @return void
*/ */
protected function assertRequestIsRedirect($request, array $params = []) protected function assertRequestIsRedirect(string $request, array $params = [])
{ {
$this->assertTrue($this->getRequestResult($request, $params)->isRedirect()); $this->assertTrue($this->getRequestResult($request, $params)->isRedirect());
} }
@ -129,7 +133,7 @@ abstract class ControllerTest extends BaseTest
* *
* @return void * @return void
*/ */
protected function assertRequestIsServerError($request, array $params = []) protected function assertRequestIsServerError(string $request, array $params = [])
{ {
$this->assertTrue($this->getRequestResult($request, $params)->isServerError()); $this->assertTrue($this->getRequestResult($request, $params)->isServerError());
} }
@ -142,7 +146,7 @@ abstract class ControllerTest extends BaseTest
* *
* @return void * @return void
*/ */
protected function assertRequestIsClientError($request, array $params = []) protected function assertRequestIsClientError(string $request, array $params = [])
{ {
$this->assertTrue($this->getRequestResult($request, $params)->isClientError()); $this->assertTrue($this->getRequestResult($request, $params)->isClientError());
} }

View File

@ -6,7 +6,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Stream\ConvertedPlaylistArchiveStream; use Alltube\Stream\ConvertedPlaylistArchiveStream;
@ -24,10 +23,10 @@ class ConvertedPlaylistArchiveStreamTest extends StreamTest
{ {
parent::setUp(); parent::setUp();
$config = Config::getInstance(); $video = $this->downloader->getVideo(
$downloader = $config->getDownloader(); 'https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'
$video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); );
$this->stream = new ConvertedPlaylistArchiveStream($downloader, $video); $this->stream = new ConvertedPlaylistArchiveStream($this->downloader, $video);
} }
} }

View File

@ -6,9 +6,9 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Controller\DownloadController; use Alltube\Controller\DownloadController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException;
use Alltube\Library\Exception\EmptyUrlException; use Alltube\Library\Exception\EmptyUrlException;
use Alltube\Library\Exception\RemuxException; use Alltube\Library\Exception\RemuxException;
use Alltube\Library\Exception\YoutubedlException; use Alltube\Library\Exception\YoutubedlException;
@ -22,7 +22,7 @@ class DownloadControllerTest extends ControllerTest
{ {
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException|SmartyException * @throws ConfigException|SmartyException|DependencyException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
@ -68,11 +68,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with streams enabled. * Test the download() function with streams enabled.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testDownloadWithStream() public function testDownloadWithStream()
{ {
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',
@ -84,11 +84,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an M3U stream. * Test the download() function with an M3U stream.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testDownloadWithM3uStream() public function testDownloadWithM3uStream()
{ {
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',
@ -104,13 +104,13 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an RTMP stream. * Test the download() function with an RTMP stream.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testDownloadWithRtmpStream() public function testDownloadWithRtmpStream()
{ {
$this->markTestIncomplete('We need to find another RTMP video.'); $this->markTestIncomplete('We need to find another RTMP video.');
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',
@ -122,11 +122,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with a remuxed video. * Test the download() function with a remuxed video.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testDownloadWithRemux() public function testDownloadWithRemux()
{ {
Config::setOptions(['remux' => true]); $config = $this->container->get('config');
$config->setOptions(['remux' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',
@ -161,7 +161,7 @@ class DownloadControllerTest extends ControllerTest
*/ */
public function testDownloadWithMissingPassword() public function testDownloadWithMissingPassword()
{ {
$this->assertRequestIsRedirect('download', ['url' => 'http://vimeo.com/68375962']); $this->assertRequestIsClientError('download', ['url' => 'http://vimeo.com/68375962']);
} }
/** /**
@ -195,11 +195,11 @@ class DownloadControllerTest extends ControllerTest
* *
* @return void * @return void
* @requires OS Linux * @requires OS Linux
* @throws ConfigException
*/ */
public function testDownloadWithPlaylist() public function testDownloadWithPlaylist()
{ {
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',
@ -211,11 +211,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an advanced conversion. * Test the download() function with an advanced conversion.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testDownloadWithAdvancedConversion() public function testDownloadWithAdvancedConversion()
{ {
Config::setOptions(['convertAdvanced' => true]); $config = $this->container->get('config');
$config->setOptions(['convertAdvanced' => true]);
$this->assertRequestIsOk( $this->assertRequestIsOk(
'download', 'download',

View File

@ -6,9 +6,9 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Controller\FrontController; use Alltube\Controller\FrontController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Exception; use Exception;
use Slim\Http\Environment; use Slim\Http\Environment;
@ -28,7 +28,7 @@ class FrontControllerTest extends ControllerTest
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException|SmartyException * @throws ConfigException|SmartyException|DependencyException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
@ -51,11 +51,11 @@ class FrontControllerTest extends ControllerTest
* Test the constructor with streams enabled. * Test the constructor with streams enabled.
* *
* @return void * @return void
* @throws ConfigException
*/ */
public function testConstructorWithStream() public function testConstructorWithStream()
{ {
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertInstanceOf(FrontController::class, new FrontController($this->container)); $this->assertInstanceOf(FrontController::class, new FrontController($this->container));
} }
@ -131,11 +131,11 @@ class FrontControllerTest extends ControllerTest
* *
* @return void * @return void
* @requires download * @requires download
* @throws ConfigException
*/ */
public function testInfoWithAudio() public function testInfoWithAudio()
{ {
Config::setOptions(['convert' => true]); $config = $this->container->get('config');
$config->setOptions(['convert' => true]);
$this->assertRequestIsRedirect( $this->assertRequestIsRedirect(
'info', 'info',
@ -148,11 +148,11 @@ class FrontControllerTest extends ControllerTest
* *
* @return void * @return void
* @requires download * @requires download
* @throws ConfigException
*/ */
public function testInfoWithVimeoAudio() public function testInfoWithVimeoAudio()
{ {
Config::setOptions(['convert' => true]); $config = $this->container->get('config');
$config->setOptions(['convert' => true]);
// So we can test the fallback to default format // So we can test the fallback to default format
$this->assertRequestIsRedirect('info', ['url' => 'https://vimeo.com/251997032', 'audio' => true]); $this->assertRequestIsRedirect('info', ['url' => 'https://vimeo.com/251997032', 'audio' => true]);
@ -163,11 +163,11 @@ class FrontControllerTest extends ControllerTest
* *
* @return void * @return void
* @requires download * @requires download
* @throws ConfigException
*/ */
public function testInfoWithUnconvertedAudio() public function testInfoWithUnconvertedAudio()
{ {
Config::setOptions(['convert' => true]); $config = $this->container->get('config');
$config->setOptions(['convert' => true]);
$this->assertRequestIsRedirect( $this->assertRequestIsRedirect(
'info', 'info',
@ -212,11 +212,11 @@ class FrontControllerTest extends ControllerTest
* *
* @return void * @return void
* @requires download * @requires download
* @throws ConfigException
*/ */
public function testInfoWithStream() public function testInfoWithStream()
{ {
Config::setOptions(['stream' => true]); $config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']); $this->assertRequestIsOk('info', ['url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU']);
$this->assertRequestIsOk( $this->assertRequestIsOk(

View File

@ -8,6 +8,7 @@ namespace Alltube\Test;
use Alltube\Controller\JsonController; use Alltube\Controller\JsonController;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException;
use Alltube\Library\Exception\YoutubedlException; use Alltube\Library\Exception\YoutubedlException;
use SmartyException; use SmartyException;
@ -18,7 +19,7 @@ class JsonControllerTest extends ControllerTest
{ {
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException|SmartyException * @throws ConfigException|SmartyException|DependencyException
*/ */
protected function setUp(): void protected function setUp(): void
{ {

View File

@ -27,7 +27,7 @@ class LocaleManagerTest extends BaseTest
protected function setUp(): void protected function setUp(): void
{ {
$_SESSION[LocaleManager::class]['locale'] = 'foo_BAR'; $_SESSION[LocaleManager::class]['locale'] = 'foo_BAR';
$this->localeManager = LocaleManager::getInstance(); $this->localeManager = new LocaleManager();
} }
/** /**
@ -38,7 +38,6 @@ class LocaleManagerTest extends BaseTest
protected function tearDown(): void protected function tearDown(): void
{ {
$this->localeManager->unsetLocale(); $this->localeManager->unsetLocale();
LocaleManager::destroyInstance();
} }
/** /**

View File

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

View File

@ -6,7 +6,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Stream\PlaylistArchiveStream; use Alltube\Stream\PlaylistArchiveStream;
@ -24,10 +23,10 @@ class PlaylistArchiveStreamTest extends StreamTest
{ {
parent::setUp(); parent::setUp();
$config = Config::getInstance(); $video = $this->downloader->getVideo(
$downloader = $config->getDownloader(); 'https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'
$video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); );
$this->stream = new PlaylistArchiveStream($downloader, $video); $this->stream = new PlaylistArchiveStream($this->downloader, $video);
} }
} }

View File

@ -6,6 +6,9 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException;
use Alltube\Library\Downloader;
use Psr\Http\Message\StreamInterface; use Psr\Http\Message\StreamInterface;
use RuntimeException; use RuntimeException;
@ -20,6 +23,25 @@ abstract class StreamTest extends BaseTest
*/ */
protected $stream; protected $stream;
/**
* Downloader class instance.
* @var Downloader
*/
protected $downloader;
/**
* Prepare tests.
* @throws ConfigException
*/
protected function setUp(): void
{
parent::setUp();
// So ffmpeg does not spam the output with broken pipe errors.
$config = new Config(['ffmpegVerbosity' => 'fatal']);
$this->downloader = $config->getDownloader();
}
/** /**
* Clean variables used in tests. * Clean variables used in tests.
* *

View File

@ -7,7 +7,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config; use Alltube\Config;
use Alltube\Exception\ConfigException;
use Alltube\Library\Downloader; use Alltube\Library\Downloader;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Alltube\Library\Exception\PopenStreamException; use Alltube\Library\Exception\PopenStreamException;
@ -39,7 +38,6 @@ class VideoStubsTest extends BaseTest
/** /**
* Initialize properties used by test. * Initialize properties used by test.
* @throws ConfigException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
@ -48,7 +46,7 @@ class VideoStubsTest extends BaseTest
PHPMockery::mock('Alltube\Library', 'popen'); PHPMockery::mock('Alltube\Library', 'popen');
PHPMockery::mock('Alltube\Library', 'fopen'); PHPMockery::mock('Alltube\Library', 'fopen');
$config = Config::getInstance(); $config = new Config();
$this->downloader = $config->getDownloader(); $this->downloader = $config->getDownloader();
$this->video = $this->downloader->getVideo('https://www.youtube.com/watch?v=XJC9_JkzugE'); $this->video = $this->downloader->getVideo('https://www.youtube.com/watch?v=XJC9_JkzugE');
} }

View File

@ -48,7 +48,8 @@ class VideoTest extends BaseTest
{ {
parent::setUp(); parent::setUp();
$config = Config::getInstance(); // So ffmpeg does not spam the output with broken pipe errors.
$config = new Config(['ffmpegVerbosity' => 'fatal']);
$this->downloader = $config->getDownloader(); $this->downloader = $config->getDownloader();
$this->format = 'best'; $this->format = 'best';
} }
@ -80,11 +81,11 @@ class VideoTest extends BaseTest
* @dataProvider remuxUrlProvider * @dataProvider remuxUrlProvider
*/ */
public function testgetUrl( public function testgetUrl(
$url, string $url,
$format, string $format,
/* @scrutinizer ignore-unused */ $filename, string $filename,
/* @scrutinizer ignore-unused */ $extension, string $extension,
$domain string $domain
) { ) {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
foreach ($video->getUrl() as $videoURL) { foreach ($video->getUrl() as $videoURL) {
@ -141,7 +142,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider ErrorUrlProvider * @dataProvider ErrorUrlProvider
*/ */
public function testgetUrlError($url) public function testgetUrlError(string $url)
{ {
$this->expectException(YoutubedlException::class); $this->expectException(YoutubedlException::class);
$video = new Video($this->downloader, $url, $this->format); $video = new Video($this->downloader, $url, $this->format);
@ -163,7 +164,7 @@ class VideoTest extends BaseTest
'googlevideo.com', 'googlevideo.com',
], ],
[ [
'https://www.youtube.com/watch?v=RJJ6FCAXvKg', 18, 'https://www.youtube.com/watch?v=RJJ6FCAXvKg', '18',
'Heart_Attack_-_Demi_Lovato_' . 'Heart_Attack_-_Demi_Lovato_' .
'Sam_Tsui_Against_The_Current-RJJ6FCAXvKg', 'Sam_Tsui_Against_The_Current-RJJ6FCAXvKg',
'mp4', 'mp4',
@ -258,7 +259,7 @@ class VideoTest extends BaseTest
* @dataProvider urlProvider * @dataProvider urlProvider
* @dataProvider m3uUrlProvider * @dataProvider m3uUrlProvider
*/ */
public function testGetJson($url, $format) public function testGetJson(string $url, string $format)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$info = $video->getJson(); $info = $video->getJson();
@ -279,7 +280,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider ErrorURLProvider * @dataProvider ErrorURLProvider
*/ */
public function testGetJsonError($url) public function testGetJsonError(string $url)
{ {
$this->expectException(YoutubedlException::class); $this->expectException(YoutubedlException::class);
$video = new Video($this->downloader, $url, $this->format); $video = new Video($this->downloader, $url, $this->format);
@ -300,7 +301,7 @@ class VideoTest extends BaseTest
* @dataProvider m3uUrlProvider * @dataProvider m3uUrlProvider
* @dataProvider remuxUrlProvider * @dataProvider remuxUrlProvider
*/ */
public function testGetFilename($url, $format, $filename, $extension) public function testGetFilename(string $url, string $format, string $filename, string $extension)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$this->assertEquals($video->getFilename(), $filename . '.' . $extension); $this->assertEquals($video->getFilename(), $filename . '.' . $extension);
@ -315,7 +316,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider ErrorUrlProvider * @dataProvider ErrorUrlProvider
*/ */
public function testGetFilenameError($url) public function testGetFilenameError(string $url)
{ {
$this->expectException(YoutubedlException::class); $this->expectException(YoutubedlException::class);
$video = new Video($this->downloader, $url, $this->format); $video = new Video($this->downloader, $url, $this->format);
@ -329,10 +330,10 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @dataProvider urlProvider
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider urlProvider
*/ */
public function testGetAudioStream($url, $format) public function testGetAudioStream(string $url, string $format)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$this->assertStream($this->downloader->getAudioStream($video)); $this->assertStream($this->downloader->getAudioStream($video));
@ -345,14 +346,14 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @throws AlltubeLibraryException|ConfigException * @throws AlltubeLibraryException
* @throws ConfigException
* @dataProvider urlProvider * @dataProvider urlProvider
*/ */
public function testGetAudioStreamFfmpegError($url, $format) public function testGetAudioStreamFfmpegError(string $url, string $format)
{ {
$this->expectException(AvconvException::class); $this->expectException(AvconvException::class);
Config::setOptions(['ffmpeg' => 'foobar']); $config = new Config(['ffmpeg' => 'foobar']);
$config = Config::getInstance();
$downloader = $config->getDownloader(); $downloader = $config->getDownloader();
$video = new Video($this->downloader, $url, $format, $this->format); $video = new Video($this->downloader, $url, $format, $this->format);
@ -369,7 +370,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider m3uUrlProvider * @dataProvider m3uUrlProvider
*/ */
public function testGetAudioStreamM3uError($url, $format) public function testGetAudioStreamM3uError(string $url, string $format)
{ {
$this->expectException(InvalidProtocolConversionException::class); $this->expectException(InvalidProtocolConversionException::class);
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
@ -426,10 +427,10 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @dataProvider m3uUrlProvider
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider m3uUrlProvider
*/ */
public function testGetM3uStream($url, $format) public function testGetM3uStream(string $url, string $format)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$this->assertStream($this->downloader->getM3uStream($video)); $this->assertStream($this->downloader->getM3uStream($video));
@ -442,10 +443,10 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @dataProvider remuxUrlProvider
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider remuxUrlProvider
*/ */
public function testGetRemuxStream($url, $format) public function testGetRemuxStream(string $url, string $format)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$this->assertStream($this->downloader->getRemuxStream($video)); $this->assertStream($this->downloader->getRemuxStream($video));
@ -461,7 +462,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider urlProvider * @dataProvider urlProvider
*/ */
public function testGetRemuxStreamWithWrongVideo($url, $format) public function testGetRemuxStreamWithWrongVideo(string $url, string $format)
{ {
$this->expectException(RemuxException::class); $this->expectException(RemuxException::class);
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
@ -478,7 +479,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider rtmpUrlProvider * @dataProvider rtmpUrlProvider
*/ */
public function testGetRtmpStream($url, $format) public function testGetRtmpStream(string $url, string $format)
{ {
$this->markTestIncomplete('We need to find another RTMP video.'); $this->markTestIncomplete('We need to find another RTMP video.');
@ -494,14 +495,14 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @throws AlltubeLibraryException|ConfigException * @throws AlltubeLibraryException
* @throws ConfigException
* @dataProvider m3uUrlProvider * @dataProvider m3uUrlProvider
*/ */
public function testGetM3uStreamFfmpegError($url, $format) public function testGetM3uStreamFfmpegError(string $url, string $format)
{ {
$this->expectException(AvconvException::class); $this->expectException(AvconvException::class);
Config::setOptions(['ffmpeg' => 'foobar']); $config = new Config(['ffmpeg' => 'foobar']);
$config = Config::getInstance();
$downloader = $config->getDownloader(); $downloader = $config->getDownloader();
$video = new Video($downloader, $url, $format); $video = new Video($downloader, $url, $format);
@ -515,10 +516,10 @@ class VideoTest extends BaseTest
* @param string $format Format * @param string $format Format
* *
* @return void * @return void
* @dataProvider urlProvider
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider urlProvider
*/ */
public function testGetConvertedStream($url, $format) public function testGetConvertedStream(string $url, string $format)
{ {
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);
$this->assertStream($this->downloader->getConvertedStream($video, 32, 'flv')); $this->assertStream($this->downloader->getConvertedStream($video, 32, 'flv'));
@ -534,7 +535,7 @@ class VideoTest extends BaseTest
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @dataProvider m3uUrlProvider * @dataProvider m3uUrlProvider
*/ */
public function testGetConvertedStreamM3uError($url, $format) public function testGetConvertedStreamM3uError(string $url, string $format)
{ {
$this->expectException(InvalidProtocolConversionException::class); $this->expectException(InvalidProtocolConversionException::class);
$video = new Video($this->downloader, $url, $format); $video = new Video($this->downloader, $url, $format);

View File

@ -28,7 +28,7 @@ class ViewFactoryTest extends BaseTest
public function testCreate() public function testCreate()
{ {
$container = new Container(); $container = new Container();
$container['locale'] = LocaleManager::getInstance(); $container['locale'] = new LocaleManager();
$view = ViewFactory::create($container); $view = ViewFactory::create($container);
$this->assertInstanceOf(Smarty::class, $view); $this->assertInstanceOf(Smarty::class, $view);
} }
@ -42,7 +42,7 @@ class ViewFactoryTest extends BaseTest
public function testCreateWithXForwardedProto() public function testCreateWithXForwardedProto()
{ {
$container = new Container(); $container = new Container();
$container['locale'] = LocaleManager::getInstance(); $container['locale'] = new LocaleManager();
$request = Request::createFromEnvironment(Environment::mock()); $request = Request::createFromEnvironment(Environment::mock());
$view = ViewFactory::create($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

@ -6,7 +6,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Alltube\Stream\YoutubeChunkStream; use Alltube\Stream\YoutubeChunkStream;
@ -19,17 +18,15 @@ class YoutubeChunkStreamTest extends StreamTest
{ {
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException
* @throws AlltubeLibraryException * @throws AlltubeLibraryException
* @throws ConfigException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$config = Config::getInstance(); $video = $this->downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
$downloader = $config->getDownloader();
$video = $downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
$this->stream = new YoutubeChunkStream($downloader->getHttpResponse($video)); $this->stream = new YoutubeChunkStream($this->downloader->getHttpResponse($video));
} }
} }

View File

@ -6,7 +6,6 @@
namespace Alltube\Test; namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException; use Alltube\Exception\ConfigException;
use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Library\Exception\AlltubeLibraryException;
use Alltube\Stream\YoutubeStream; use Alltube\Stream\YoutubeStream;
@ -19,17 +18,16 @@ class YoutubeStreamTest extends StreamTest
{ {
/** /**
* Prepare tests. * Prepare tests.
* @throws ConfigException|AlltubeLibraryException * @throws AlltubeLibraryException
* @throws ConfigException
*/ */
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$config = Config::getInstance(); $video = $this->downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '135');
$downloader = $config->getDownloader();
$video = $downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '135');
$this->stream = new YoutubeStream($downloader, $video); $this->stream = new YoutubeStream($this->downloader, $video);
} }
/** /**