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-XSS-Protection "1; mode=block"
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/fonts.css>; rel=preload, </css/style.css>; rel=preload" "expr=%{CONTENT_TYPE} =~ m#text/html#"
Header add Link "</css/style.css>; rel=preload" "expr=%{CONTENT_TYPE} =~ m#text/html#"
</ifmodule>

View File

@ -76,6 +76,12 @@ You will need PHP 7.2 (or higher) and the following PHP modules:
## 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
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 Jawira\CaseConverter\CaseConverterException;
use Jean85\PrettyVersions;
use PackageVersions\Versions;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\Yaml\Yaml;
use Jawira\CaseConverter\Convert;
@ -20,12 +19,6 @@ use Jawira\CaseConverter\Convert;
*/
class Config
{
/**
* Singleton instance.
*
* @var Config|null
*/
private static $instance;
/**
* youtube-dl binary path.
@ -140,17 +133,32 @@ class Config
*/
public $debug = false;
/**
* Default to audio.
*
* @var bool
*/
public $defaultAudio = false;
/**
* Disable audio conversion from/to seeker.
*
* @var bool
*/
public $convertSeek = true;
/**
* Config constructor.
*
* @param mixed[] $options Options
* @throws ConfigException
*/
private function __construct(array $options = [])
public function __construct(array $options = [])
{
$this->applyOptions($options);
$this->getEnv();
$localeManager = LocaleManager::getInstance();
$this->validateOptions();
$localeManager = new LocaleManager();
if (empty($this->genericFormats)) {
// We don't put this in the class definition so it can be detected by xgettext.
@ -185,7 +193,7 @@ class Config
*
* @return string
*/
public static function addHttpToFormat($format)
public static function addHttpToFormat(string $format)
{
$newFormat = [];
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.
*
* @param string $file Path to the YAML file
* @return void
* @return Config
* @throws ConfigException
*/
public static function setFile($file)
public static function fromFile(string $file)
{
if (is_file($file)) {
$options = Yaml::parse(strval(file_get_contents($file)));
self::$instance = new self($options);
self::$instance->validateOptions();
return new self(Yaml::parse(strval(file_get_contents($file))));
} else {
throw new ConfigException("Can't find config file at " . $file);
}
@ -293,29 +285,13 @@ class Config
* Manually set some options.
*
* @param mixed[] $options Options (see `config/config.example.yml` for available options)
* @param bool $update True to update an existing instance
* @return void
* @throws ConfigException
*/
public static function setOptions(array $options, $update = true)
public function setOptions(array $options)
{
if ($update) {
$config = self::getInstance();
$config->applyOptions($options);
$config->validateOptions();
} else {
self::$instance = new self($options);
}
}
/**
* Destroy singleton instance.
*
* @return void
*/
public static function destroyInstance()
{
self::$instance = null;
$this->applyOptions($options);
$this->validateOptions();
}
/**
@ -340,7 +316,7 @@ class Config
*/
public function getAppVersion()
{
$version = PrettyVersions::getVersion(Versions::ROOT_PACKAGE_NAME);
$version = PrettyVersions::getRootPackageVersion();
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\SessionManager;
use Aura\Session\Segment;
use Consolidation\Log\Logger;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Response;
@ -70,6 +71,11 @@ abstract class BaseController
*/
protected $downloader;
/**
* @var Logger
*/
protected $logger;
/**
* BaseController constructor.
*
@ -77,12 +83,14 @@ abstract class BaseController
*/
public function __construct(ContainerInterface $container)
{
$this->config = Config::getInstance();
$this->config = $container->get('config');
$this->container = $container;
$session = SessionManager::getSession();
$this->sessionSegment = $session->getSegment(self::class);
$this->localeManager = $this->container->get('locale');
$this->downloader = $this->config->getDownloader();
$this->logger = $this->container->get('logger');
$this->downloader->setLogger($this->logger);
if (!$this->config->stream) {
// Force HTTP if stream is not enabled.
@ -137,7 +145,7 @@ abstract class BaseController
*
* @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);

View File

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

View File

@ -6,11 +6,13 @@
namespace Alltube\Controller;
use Alltube\CspMiddleware;
use Alltube\Library\Exception\PasswordException;
use Alltube\Library\Exception\AlltubeLibraryException;
use Alltube\Library\Exception\WrongPasswordException;
use Alltube\Locale;
use Exception;
use Slim\Http\StatusCode;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Throwable;
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
*/
protected function displayError(Request $request, Response $response, $message)
protected function displayError(Request $request, Response $response, string $message)
{
$this->view->render(
$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)
{
$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) {
$renderer = new HtmlErrorRenderer(true);
$exception = $renderer->render($error);

View File

@ -9,6 +9,7 @@ namespace Alltube\Controller;
use Alltube\Library\Exception\AlltubeLibraryException;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\StatusCode;
/**
* Controller that returns JSON.
@ -38,7 +39,7 @@ class JsonController extends BaseController
return $response->withJson($this->video->getJson());
} else {
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;
/**
* Class ConfigException
* @package Alltube\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
*/
public function __construct($locale)
public function __construct(string $locale)
{
$parse = AcceptLanguage::parse($locale);
$this->language = $parse[1]['language'];

View File

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

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.
*
* @param string $data Data
* @param mixed $data Data
*
* @return void
*/
@ -99,7 +99,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/**
* 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
*/
@ -171,7 +171,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/**
* 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
*/
@ -228,7 +228,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/**
* 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
*
* @return void
@ -272,7 +272,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
/**
* Read data from the stream.
*
* @param int $count Number of bytes to read
* @param mixed $count Number of bytes to read
*
* @return string|false
* @throws AlltubeLibraryException

View File

@ -35,7 +35,7 @@ class YoutubeChunkStream implements StreamInterface
/**
* 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
*/
@ -121,7 +121,7 @@ class YoutubeChunkStream implements StreamInterface
/**
* 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
*
* @return void
@ -154,7 +154,7 @@ class YoutubeChunkStream implements StreamInterface
/**
* 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
*/
@ -186,7 +186,7 @@ class YoutubeChunkStream implements StreamInterface
/**
* 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
*/

View File

@ -42,7 +42,7 @@ class UglyRouter extends Router
/**
* 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[] $queryParams Optional query string parameters
*

View File

@ -8,6 +8,7 @@ namespace Alltube;
use Psr\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Uri;
use Slim\Views\Smarty;
use Slim\Views\SmartyPlugins;
use SmartyException;
@ -21,7 +22,7 @@ class ViewFactory
* Create Smarty view object.
*
* @param ContainerInterface $container Slim dependency container
* @param Request $request PSR-7 request
* @param Request|null $request PSR-7 request
*
* @return Smarty
* @throws SmartyException
@ -33,14 +34,30 @@ class ViewFactory
}
$view = new Smarty(__DIR__ . '/../templates/');
/** @var Uri $uri */
$uri = $request->getUri();
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 */
$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', 'base_url', [$smartyPlugins, 'baseUrl']);
$view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);

View File

@ -1,55 +1,9 @@
{
"name": "rudloff/alltube",
"description": "HTML GUI for youtube-dl",
"license": "GPL-3.0-only",
"homepage": "http://alltubedownload.net/",
"type": "project",
"require": {
"ext-intl": "*",
"ext-json": "*",
"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"
}
}
}
],
"description": "HTML GUI for youtube-dl",
"homepage": "http://alltubedownload.net/",
"license": "GPL-3.0-only",
"authors": [
{
"name": "Pierre Rudloff",
@ -64,29 +18,79 @@
"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": {
"psr-4": {
"Alltube\\": "classes/",
"Alltube\\Stream\\": "classes/streams/",
"Alltube\\Exception\\": "classes/exceptions/",
"Alltube\\Controller\\": "controllers/",
"Alltube\\": "classes/"
}
},
"autoload-dev": {
"psr-4": {
"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": {
"lint": "grumphp run --ansi",
"test": "phpunit",
"release": "robo release --ansi",
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
],
"update-locales": [
"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"
},
"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.
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 {
background-color: #ebebeb;
background-image: url("../img/fond.jpg");
}
.page {
font-family: "Open Sans", sans-serif;
font-weight: 400;
text-align: center;
}
.small-font {
font-size: 13px;
}
.large-font {
font-size:24px;
}
/* Header */
header {

View File

@ -1,16 +1,19 @@
---
parameters:
ascii: ~
grumphp:
extensions:
- ComposerDanglingLockedDeps\GrumPHP\Loader
ascii:
succeeded: ~
failed: ~
tasks:
jsonlint: ~
xmllint: ~
yamllint: ~
composer: ~
securitychecker: ~
composer_normalize: ~
composer_dangling_locked_deps: ~
phpcs:
standard: PSR12
ignore_patterns:
- RoboFile.php
phpstan:
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 ""
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
msgid "Code by @dev"
msgstr ""
@ -25,12 +46,44 @@ msgstr ""
msgid "Donate"
msgstr ""
#: templates/inc/header.tpl:4
msgid "Switch language"
#: templates/password.tpl:5
msgid "This video is protected"
msgstr ""
#: templates/inc/header.tpl:8
msgid "Set language"
#: 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 ""
#: templates/info.tpl:11
@ -81,23 +134,6 @@ msgstr ""
msgid "kbit/s audio"
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
msgid "An error occurred"
msgstr ""
@ -106,106 +142,71 @@ msgstr ""
msgid "Please check the URL of your video."
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 ""
#: 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"
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:109
msgid "Supported websites"
msgstr ""
#: classes/Config.php:158
msgid "Best"
msgstr ""
#: classes/Config.php:159
msgid "Remux best video with best audio"
msgstr ""
#: classes/Config.php:160
msgid "Worst"
msgstr ""
#: controllers/DownloadController.php:63 controllers/FrontController.php:164
#: classes/Controller/DownloadController.php:63
#: classes/Controller/FrontController.php:164
msgid "Wrong password"
msgstr ""
#: controllers/DownloadController.php:68
#: classes/Controller/DownloadController.php:68
msgid "Conversion of playlists is not supported."
msgstr ""
#: controllers/DownloadController.php:75
#: classes/Controller/DownloadController.php:75
msgid "Conversion of M3U8 files is not supported."
msgstr ""
#: controllers/DownloadController.php:81
#: classes/Controller/DownloadController.php:81
msgid "Conversion of DASH segments is not supported."
msgstr ""
#: controllers/FrontController.php:63
#: classes/Controller/FrontController.php:63
msgid ""
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
msgstr ""
#: controllers/FrontController.php:110
#: classes/Controller/FrontController.php:110
msgid ""
"List of all supported websites from which Alltube Download can extract video "
"or audio files"
msgstr ""
#: controllers/FrontController.php:136
#: classes/Controller/FrontController.php:136
msgid "Password prompt"
msgstr ""
#: controllers/FrontController.php:138
#: classes/Controller/FrontController.php:138
msgid ""
"You need a password in order to download this video with Alltube Download"
msgstr ""
#: controllers/FrontController.php:172
#: classes/Controller/FrontController.php:172
msgid "Video download"
msgstr ""
#: controllers/FrontController.php:174
#: classes/Controller/FrontController.php:174
msgid "Download video from @extractor"
msgstr ""
#: controllers/FrontController.php:180
#: classes/Controller/FrontController.php:180
msgid "Download @title from @extractor"
msgstr ""
#: controllers/FrontController.php:253
#: classes/Controller/FrontController.php:253
msgid "Error"
msgstr ""

150
index.php
View File

@ -2,115 +2,97 @@
require_once __DIR__ . '/vendor/autoload.php';
use Alltube\Config;
use Alltube\ConfigFactory;
use Alltube\Controller\DownloadController;
use Alltube\Controller\FrontController;
use Alltube\Controller\JsonController;
use Alltube\LocaleManager;
use Alltube\CspMiddleware;
use Alltube\ErrorHandler;
use Alltube\LocaleManagerFactory;
use Alltube\LocaleMiddleware;
use Alltube\UglyRouter;
use Alltube\LoggerFactory;
use Alltube\RouterPathMiddleware;
use Alltube\ViewFactory;
use Slim\App;
use Slim\Container;
use Symfony\Component\ErrorHandler\Debug;
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
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 {
// 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);
} catch (SmartyException $e) {
die('Could not load Smarty: ' . $e->getMessage());
}
// Controllers.
$frontController = new FrontController($container);
$jsonController = new JsonController($container);
$downloadController = new DownloadController($container);
// Logger.
$container['logger'] = LoggerFactory::create($container);
// Error handling.
$container['errorHandler'] = [$frontController, 'error'];
$container['phpErrorHandler'] = [$frontController, 'error'];
// Middlewares.
$app->add(new LocaleMiddleware($container));
$app->add(new RouterPathMiddleware($container));
$app->add(new CspMiddleware($container));
// Routes.
$app->get(
'/',
[$frontController, 'index']
)->setName('index');
// Controllers.
$frontController = new FrontController($container);
$jsonController = new JsonController($container);
$downloadController = new DownloadController($container);
$app->get(
'/extractors',
[$frontController, 'extractors']
)->setName('extractors');
// Error handling.
$container['errorHandler'] = [$frontController, 'error'];
$container['phpErrorHandler'] = [$frontController, 'error'];
$container['notFoundHandler'] = [$frontController, 'notFound'];
$container['notAllowedHandler'] = [$frontController, 'notAllowed'];
$app->any(
'/info',
[$frontController, 'info']
)->setName('info');
// Routes.
$app->get(
'/',
[$frontController, 'index']
)->setName('index');
$app->any(
'/watch',
[$frontController, 'info']
);
$app->get(
'/extractors',
[$frontController, 'extractors']
)->setName('extractors');
$app->any(
'/download',
[$downloadController, 'download']
)->setName('download');
$app->any(
'/info',
[$frontController, 'info']
)->setName('info');
$app->get(
'/locale/{locale}',
[$frontController, 'locale']
)->setName('locale');
$app->any(
'/watch',
[$frontController, 'info']
);
$app->get(
'/json',
[$jsonController, 'json']
)->setName('json');
$app->any(
'/download',
[$downloadController, 'download']
)->setName('download');
$app->get(
'/locale/{locale}',
[$frontController, 'locale']
)->setName('locale');
$app->get(
'/json',
[$jsonController, 'json']
)->setName('json');
try {
$app->run();
} catch (SmartyException $e) {
die('Smarty could not compile the template file: ' . $e->getMessage());
} catch (Throwable $e) {
// Last resort if the error has not been caught by the error handler for some reason.
die('Error when starting the app: ' . htmlentities($e->getMessage()));
ErrorHandler::handle($e);
}

View File

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

View File

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

View File

@ -8,7 +8,7 @@
<meta name="twitter:description" content="{$description|escape}"/>
<meta property="og:description" content="{$description|escape}"/>
{/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"/>
<title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title>
<link rel="canonical" href="{$canonical}"/>
@ -23,4 +23,5 @@
<link rel="manifest" href="{base_url}/resources/manifest.json"/>
<meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/>
</head>
<body class="{$class}">
<body>
<div class="page {$class}">

View File

@ -20,10 +20,11 @@
{if $config->convert}
<div class="mp3 small-font">
<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>
{t}Audio only (MP3){/t}
</label>
{if $config->convertSeek}
<div class="seekOptions">
<label for="from">{t}From{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
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+)?"
placeholder="HH:MM:SS" value="" name="to" id="to"/>
</div>
{/if}
</div>
</div>
{/if}

View File

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

View File

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

View File

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

View File

@ -6,7 +6,6 @@
namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException;
use Alltube\Stream\ConvertedPlaylistArchiveStream;
@ -24,10 +23,10 @@ class ConvertedPlaylistArchiveStreamTest extends StreamTest
{
parent::setUp();
$config = Config::getInstance();
$downloader = $config->getDownloader();
$video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ');
$video = $this->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;
use Alltube\Config;
use Alltube\Controller\DownloadController;
use Alltube\Exception\ConfigException;
use Alltube\Exception\DependencyException;
use Alltube\Library\Exception\EmptyUrlException;
use Alltube\Library\Exception\RemuxException;
use Alltube\Library\Exception\YoutubedlException;
@ -22,7 +22,7 @@ class DownloadControllerTest extends ControllerTest
{
/**
* Prepare tests.
* @throws ConfigException|SmartyException
* @throws ConfigException|SmartyException|DependencyException
*/
protected function setUp(): void
{
@ -68,11 +68,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with streams enabled.
*
* @return void
* @throws ConfigException
*/
public function testDownloadWithStream()
{
Config::setOptions(['stream' => true]);
$config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk(
'download',
@ -84,11 +84,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an M3U stream.
*
* @return void
* @throws ConfigException
*/
public function testDownloadWithM3uStream()
{
Config::setOptions(['stream' => true]);
$config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk(
'download',
@ -104,13 +104,13 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an RTMP stream.
*
* @return void
* @throws ConfigException
*/
public function testDownloadWithRtmpStream()
{
$this->markTestIncomplete('We need to find another RTMP video.');
Config::setOptions(['stream' => true]);
$config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk(
'download',
@ -122,11 +122,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with a remuxed video.
*
* @return void
* @throws ConfigException
*/
public function testDownloadWithRemux()
{
Config::setOptions(['remux' => true]);
$config = $this->container->get('config');
$config->setOptions(['remux' => true]);
$this->assertRequestIsOk(
'download',
@ -161,7 +161,7 @@ class DownloadControllerTest extends ControllerTest
*/
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
* @requires OS Linux
* @throws ConfigException
*/
public function testDownloadWithPlaylist()
{
Config::setOptions(['stream' => true]);
$config = $this->container->get('config');
$config->setOptions(['stream' => true]);
$this->assertRequestIsOk(
'download',
@ -211,11 +211,11 @@ class DownloadControllerTest extends ControllerTest
* Test the download() function with an advanced conversion.
*
* @return void
* @throws ConfigException
*/
public function testDownloadWithAdvancedConversion()
{
Config::setOptions(['convertAdvanced' => true]);
$config = $this->container->get('config');
$config->setOptions(['convertAdvanced' => true]);
$this->assertRequestIsOk(
'download',

View File

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

View File

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

View File

@ -27,7 +27,7 @@ class LocaleManagerTest extends BaseTest
protected function setUp(): void
{
$_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
{
$this->localeManager->unsetLocale();
LocaleManager::destroyInstance();
}
/**

View File

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

View File

@ -6,7 +6,6 @@
namespace Alltube\Test;
use Alltube\Config;
use Alltube\Exception\ConfigException;
use Alltube\Stream\PlaylistArchiveStream;
@ -24,10 +23,10 @@ class PlaylistArchiveStreamTest extends StreamTest
{
parent::setUp();
$config = Config::getInstance();
$downloader = $config->getDownloader();
$video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ');
$video = $this->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;
use Alltube\Config;
use Alltube\Exception\ConfigException;
use Alltube\Library\Downloader;
use Psr\Http\Message\StreamInterface;
use RuntimeException;
@ -20,6 +23,25 @@ abstract class StreamTest extends BaseTest
*/
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.
*

View File

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

View File

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

View File

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