Merge branch 'release/3.1.0'
This commit is contained in:
commit
36a91c8d4d
64 changed files with 2006 additions and 1336 deletions
24
.github/workflows/ci.yml
vendored
Normal file
24
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
name: Tests
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- '7.3'
|
||||
- '7.4'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use PHP ${{ matrix.php-version }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
- run: composer install --no-progress
|
||||
- run: composer check-platform-reqs
|
||||
- run: composer lint
|
||||
- run: composer test
|
|
@ -18,9 +18,6 @@ FileETag None
|
|||
<ifmodule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC]
|
||||
RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [QSA,L]
|
||||
</ifmodule>
|
||||
|
|
12
.travis.yml
12
.travis.yml
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
language: php
|
||||
php: 7.3
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- language-pack-fr
|
||||
install: composer install --no-progress
|
||||
script:
|
||||
- composer check-platform-reqs
|
||||
- composer lint
|
||||
- composer test
|
|
@ -1,6 +1,6 @@
|
|||
# AllTube Download
|
||||
|
||||
HTML GUI for youtube-dl ([alltubedownload.net](http://alltubedownload.net/))
|
||||
HTML GUI for youtube-dl
|
||||
|
||||
![Screenshot](img/screenshot.png "AllTube GUI screenshot")
|
||||
|
||||
|
@ -79,6 +79,7 @@ If you want to serve the application under a basepath and/or with a different in
|
|||
* X-Forwarded-Host (ex. `another.domain.com`)
|
||||
* X-Forwarded-Path (ex: `/alltube`)
|
||||
* X-Forwarded-Port (ex: `5555`)
|
||||
* X-Forwarded-Proto (ex: `https`)
|
||||
|
||||
### Apache
|
||||
|
||||
|
@ -164,7 +165,7 @@ so that you can reuse it in your projects.
|
|||
## JSON API
|
||||
|
||||
We also provide a JSON API that you can use like this:
|
||||
[/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ](https://alltubedownload.net/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ)
|
||||
`/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ`
|
||||
|
||||
It returns a JSON object generated by youtube-dl.
|
||||
You can find a list of all the properties [in the youtube-dl documentation](https://github.com/ytdl-org/youtube-dl#output-template).
|
||||
|
|
4
app.json
4
app.json
|
@ -2,7 +2,6 @@
|
|||
"name": "AllTube Download",
|
||||
"description": "HTML GUI for youtube-dl",
|
||||
"repository": "https://github.com/Rudloff/alltube.git",
|
||||
"logo": "https://alltubedownload.net/img/logo.png",
|
||||
"keywords": [
|
||||
"alltube",
|
||||
"download",
|
||||
|
@ -28,6 +27,5 @@
|
|||
"value": "false",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"website": "https://alltubedownload.net/"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ class App extends \Slim\App
|
|||
// Middlewares.
|
||||
$this->add(new LocaleMiddleware($container));
|
||||
$this->add(new CspMiddleware($container));
|
||||
$this->add(new LinkHeaderMiddleware($container));
|
||||
$this->add(new LinkHeaderMiddleware());
|
||||
$this->add(new RouterPathMiddleware($container));
|
||||
|
||||
// Controllers.
|
||||
|
@ -94,7 +94,7 @@ class App extends \Slim\App
|
|||
|
||||
$this->any(
|
||||
'/watch',
|
||||
[$frontController, 'info']
|
||||
[$frontController, 'watch']
|
||||
);
|
||||
|
||||
$this->any(
|
||||
|
|
|
@ -154,7 +154,7 @@ class Config
|
|||
/**
|
||||
* Config constructor.
|
||||
*
|
||||
* @param mixed[] $options Options
|
||||
* @param scalar[]|scalar[][]|null[] $options Options
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
|
@ -205,7 +205,7 @@ class Config
|
|||
* @throws ConfigException If Python is missing
|
||||
* @throws ConfigException If youtube-dl is missing
|
||||
*/
|
||||
private function validateOptions()
|
||||
private function validateOptions(): void
|
||||
{
|
||||
if (!is_file($this->youtubedl)) {
|
||||
throw new ConfigException("Can't find youtube-dl at " . $this->youtubedl);
|
||||
|
@ -222,11 +222,11 @@ class Config
|
|||
/**
|
||||
* Apply the provided options.
|
||||
*
|
||||
* @param mixed[] $options Options
|
||||
* @param scalar[]|scalar[][]|null[] $options Options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function applyOptions(array $options)
|
||||
private function applyOptions(array $options): void
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
if (isset($this->$option) && isset($value)) {
|
||||
|
@ -243,7 +243,7 @@ class Config
|
|||
* @return void
|
||||
* @throws ConfigException
|
||||
*/
|
||||
private function getEnv()
|
||||
private function getEnv(): void
|
||||
{
|
||||
foreach (get_object_vars($this) as $prop => $value) {
|
||||
try {
|
||||
|
@ -278,11 +278,11 @@ class Config
|
|||
/**
|
||||
* Manually set some options.
|
||||
*
|
||||
* @param mixed[] $options Options (see `config/config.example.yml` for available options)
|
||||
* @param scalar[]|scalar[][]|null[] $options Options (see `config/config.example.yml` for available options)
|
||||
* @return void
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
public function setOptions(array $options): void
|
||||
{
|
||||
$this->applyOptions($options);
|
||||
$this->validateOptions();
|
||||
|
|
|
@ -169,10 +169,8 @@ abstract class BaseController
|
|||
*/
|
||||
protected function getVideoPageUrl(Request $request): string
|
||||
{
|
||||
$url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
|
||||
|
||||
// Prevent SSRF attacks.
|
||||
$parts = Url::validateUrl($url, new Options());
|
||||
$parts = Url::validateUrl($request->getQueryParam('url'), new Options());
|
||||
|
||||
return $parts['url'];
|
||||
}
|
||||
|
|
|
@ -220,13 +220,12 @@ class DownloadController extends BaseController
|
|||
if ($request->isGet()) {
|
||||
$response = $response->withBody($body);
|
||||
}
|
||||
$response = $response->withHeader(
|
||||
|
||||
return $response->withHeader(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="' .
|
||||
$this->video->getFilename() . '"'
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,8 @@ use Alltube\Middleware\CspMiddleware;
|
|||
use Exception;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\StatusCode;
|
||||
use Slim\Http\Uri;
|
||||
use stdClass;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Throwable;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
@ -146,7 +148,7 @@ class FrontController extends BaseController
|
|||
* @return Response HTTP response
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getInfoResponse(Request $request, Response $response)
|
||||
private function getInfoResponse(Request $request, Response $response): Response
|
||||
{
|
||||
try {
|
||||
$this->video->getJson();
|
||||
|
@ -176,6 +178,50 @@ class FrontController extends BaseController
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
$formats = [];
|
||||
$genericFormatsLabel = $this->localeManager->t('Generic formats');
|
||||
$detailedFormatsLabel = $this->localeManager->t('Detailed formats');
|
||||
|
||||
foreach ($this->config->genericFormats as $id => $genericFormat) {
|
||||
$formats[$genericFormatsLabel][$id] = $this->localeManager->t($genericFormat);
|
||||
}
|
||||
|
||||
$json = $this->video->getJson();
|
||||
if (isset($json->formats)) {
|
||||
/** @var stdClass $format */
|
||||
foreach ($json->formats as $format) {
|
||||
if ($this->config->stream || in_array($format->protocol, ['http', 'https'])) {
|
||||
$formatParts = [
|
||||
// File extension
|
||||
$format->ext,
|
||||
];
|
||||
|
||||
if (isset($format->width) || isset($format->height)) {
|
||||
// Video dimensions
|
||||
$formatParts[] = implode('x', array_filter([$format->width, $format->height]));
|
||||
}
|
||||
|
||||
if (isset($format->filesize)) {
|
||||
// File size
|
||||
$formatParts[] = round($format->filesize / 1000000, 2) . ' MB';
|
||||
}
|
||||
|
||||
if (isset($format->format_note)) {
|
||||
// Format name
|
||||
$formatParts[] = $format->format_note;
|
||||
}
|
||||
|
||||
if (isset($format->format_id)) {
|
||||
// Format ID
|
||||
$formatParts[] = '(' . $format->format_id . ')';
|
||||
}
|
||||
|
||||
$formats[$detailedFormatsLabel][$format->format_id] = implode(' ', $formatParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->render(
|
||||
$response,
|
||||
$template,
|
||||
|
@ -185,6 +231,7 @@ class FrontController extends BaseController
|
|||
'title' => $title,
|
||||
'description' => $description,
|
||||
'defaultFormat' => $this->defaultFormat,
|
||||
'formats' => $formats
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -302,4 +349,24 @@ class FrontController extends BaseController
|
|||
return $this->displayError($request, $response, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route that mimics YouTube video URLs ("/watch?v=foo")
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function watch(Request $request, Response $response): Response
|
||||
{
|
||||
// We build a full YouTube URL from the video ID.
|
||||
$youtubeUri = Uri::createFromString('https://www.youtube.com/watch')
|
||||
->withQuery(http_build_query(['v' => $request->getQueryParam('v')]));
|
||||
|
||||
// Then pass it to the info route.
|
||||
return $response->withRedirect(
|
||||
Uri::createFromString($this->router->pathFor('info'))
|
||||
->withQuery(http_build_query(['url' => strval($youtubeUri)]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
namespace Alltube\Controller;
|
||||
|
||||
use Alltube\Library\Exception\AlltubeLibraryException;
|
||||
use Exception;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
@ -25,6 +24,7 @@ class JsonController extends BaseController
|
|||
* @param Response $response PSR-7 response
|
||||
*
|
||||
* @return Response HTTP response
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
public function json(Request $request, Response $response): Response
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Alltube;
|
||||
|
||||
use Slim\Http\StatusCode;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
|
@ -17,7 +18,7 @@ class ErrorHandler
|
|||
* @param Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public static function handle(Throwable $e)
|
||||
public static function handle(Throwable $e): void
|
||||
{
|
||||
error_log($e);
|
||||
|
||||
|
@ -29,7 +30,7 @@ class ErrorHandler
|
|||
http_response_code($exception->getStatusCode());
|
||||
die($exception->getAsString());
|
||||
} else {
|
||||
http_response_code(500);
|
||||
http_response_code(StatusCode::HTTP_INTERNAL_SERVER_ERROR);
|
||||
die('Error when starting the app: ' . htmlentities($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Alltube\Factory;
|
|||
|
||||
use Alltube\Exception\DependencyException;
|
||||
use Alltube\LocaleManager;
|
||||
use Locale;
|
||||
use Slim\Container;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +21,7 @@ class LocaleManagerFactory
|
|||
*/
|
||||
public static function create(Container $container): LocaleManager
|
||||
{
|
||||
if (!class_exists('Locale')) {
|
||||
if (!class_exists(Locale::class)) {
|
||||
throw new DependencyException('You need to install the intl extension for PHP.');
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use Consolidation\Log\LoggerManager;
|
|||
use Consolidation\Log\LogOutputStyler;
|
||||
use Slim\Container;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class LoggerFactory
|
||||
|
@ -23,9 +24,9 @@ class LoggerFactory
|
|||
{
|
||||
$config = $container->get('config');
|
||||
if ($config->debug) {
|
||||
$verbosity = ConsoleOutput::VERBOSITY_DEBUG;
|
||||
$verbosity = OutputInterface::VERBOSITY_DEBUG;
|
||||
} else {
|
||||
$verbosity = ConsoleOutput::VERBOSITY_NORMAL;
|
||||
$verbosity = OutputInterface::VERBOSITY_NORMAL;
|
||||
}
|
||||
|
||||
$loggerManager = new LoggerManager();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Alltube\Factory;
|
||||
|
||||
use Aura\Session\Session;
|
||||
use Aura\Session\SessionFactory as AuraSessionFactory;
|
||||
use Slim\Container;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +24,7 @@ class SessionFactory
|
|||
*/
|
||||
public static function create(Container $container): Session
|
||||
{
|
||||
$session_factory = new \Aura\Session\SessionFactory();
|
||||
$session_factory = new AuraSessionFactory();
|
||||
$session = $session_factory->newInstance($_COOKIE);
|
||||
|
||||
$session->setCookieParams(['httponly' => true]);
|
||||
|
|
|
@ -20,22 +20,6 @@ use SmartyException;
|
|||
*/
|
||||
class ViewFactory
|
||||
{
|
||||
/**
|
||||
* Generate the canonical URL of the current page.
|
||||
*
|
||||
* @param Request $request PSR-7 Request
|
||||
*
|
||||
* @return string URL
|
||||
*/
|
||||
private static function getCanonicalUrl(Request $request): string
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $request->getUri();
|
||||
|
||||
return $uri->withBasePath('')
|
||||
->withHost('alltubedownload.net')
|
||||
->withScheme('https');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uri $uri
|
||||
|
@ -66,22 +50,13 @@ class ViewFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Create Smarty view object.
|
||||
* Create a URI suitable for templates.
|
||||
*
|
||||
* @param ContainerInterface $container Slim dependency container
|
||||
* @param Request|null $request PSR-7 request
|
||||
*
|
||||
* @return Smarty
|
||||
* @throws SmartyException
|
||||
* @param Request $request
|
||||
* @return Uri
|
||||
*/
|
||||
public static function create(ContainerInterface $container, Request $request = null): Smarty
|
||||
public static function prepareUri(Request $request): Uri
|
||||
{
|
||||
if (!isset($request)) {
|
||||
$request = $container->get('request');
|
||||
}
|
||||
|
||||
$view = new Smarty($container->get('root_path') . '/templates/');
|
||||
|
||||
/** @var Uri $uri */
|
||||
$uri = $request->getUri();
|
||||
if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) {
|
||||
|
@ -101,25 +76,45 @@ class ViewFactory
|
|||
$uri = $uri->withBasePath($path);
|
||||
}
|
||||
|
||||
return self::cleanBasePath($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Smarty view object.
|
||||
*
|
||||
* @param ContainerInterface $container Slim dependency container
|
||||
* @param Request|null $request PSR-7 request
|
||||
*
|
||||
* @return Smarty
|
||||
* @throws SmartyException
|
||||
*/
|
||||
public static function create(ContainerInterface $container, Request $request = null): Smarty
|
||||
{
|
||||
if (!isset($request)) {
|
||||
$request = $container->get('request');
|
||||
}
|
||||
|
||||
$view = new Smarty($container->get('root_path') . '/templates/');
|
||||
|
||||
$uri = self::prepareUri($request);
|
||||
|
||||
/** @var LocaleManager $localeManager */
|
||||
$localeManager = $container->get('locale');
|
||||
|
||||
$uri = self::cleanBasePath($uri);
|
||||
|
||||
$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->offsetSet('canonical', self::getCanonicalUrl($request));
|
||||
$view->offsetSet('locale', $container->get('locale'));
|
||||
$view->offsetSet('config', $container->get('config'));
|
||||
$view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl());
|
||||
|
||||
if ($container->has('debugbar')) {
|
||||
$debugBar = $container->get('debugbar');
|
||||
|
||||
$debugBar->addCollector(new SmartyCollector($view->getSmarty()));
|
||||
$collector = new SmartyCollector($view->getSmarty());
|
||||
$collector->useHtmlVarDumper();
|
||||
$debugBar->addCollector($collector);
|
||||
|
||||
$view->offsetSet(
|
||||
'debug_render',
|
||||
|
|
|
@ -114,7 +114,7 @@ class LocaleManager
|
|||
* @param Locale $locale Locale
|
||||
* @return void
|
||||
*/
|
||||
public function setLocale(Locale $locale)
|
||||
public function setLocale(Locale $locale): void
|
||||
{
|
||||
$this->translator->setLocale($locale->getIso15897());
|
||||
$this->curLocale = $locale;
|
||||
|
@ -125,7 +125,7 @@ class LocaleManager
|
|||
* Unset the current locale.
|
||||
* @return void
|
||||
*/
|
||||
public function unsetLocale()
|
||||
public function unsetLocale(): void
|
||||
{
|
||||
$this->translator->setLocale(self::DEFAULT_LOCALE);
|
||||
$this->curLocale = null;
|
||||
|
@ -135,14 +135,14 @@ class LocaleManager
|
|||
/**
|
||||
* Smarty "t" block.
|
||||
*
|
||||
* @param mixed[] $params Block parameters
|
||||
* @param string[]|string[][] $params Block parameters
|
||||
* @param string|null $text Block content
|
||||
*
|
||||
* @return string Translated string
|
||||
*/
|
||||
public function smartyTranslate(array $params, string $text = null): string
|
||||
{
|
||||
if (isset($params['params'])) {
|
||||
if (isset($params['params']) && is_array($params['params'])) {
|
||||
return $this->t($text, $params['params']);
|
||||
} else {
|
||||
return $this->t($text);
|
||||
|
@ -154,7 +154,7 @@ class LocaleManager
|
|||
*
|
||||
* @param string|null $string $string String to translate
|
||||
*
|
||||
* @param mixed[] $params
|
||||
* @param string[] $params
|
||||
* @return string Translated string
|
||||
*/
|
||||
public function t(string $string = null, array $params = []): string
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
namespace Alltube\Middleware;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Alltube\Factory\ViewFactory;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Router;
|
||||
|
||||
/**
|
||||
* Class LinkHeaderMiddleware
|
||||
|
@ -13,19 +12,6 @@ use Slim\Router;
|
|||
*/
|
||||
class LinkHeaderMiddleware
|
||||
{
|
||||
/**
|
||||
* @var Router
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* LinkHeaderMiddleware constructor.
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->router = $container->get('router');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
|
@ -35,12 +21,19 @@ class LinkHeaderMiddleware
|
|||
*/
|
||||
public function __invoke(Request $request, Response $response, callable $next)
|
||||
{
|
||||
$uri = ViewFactory::prepareUri($request);
|
||||
|
||||
$response = $response->withHeader(
|
||||
'Link',
|
||||
'<' . $this->router->getBasePath() . '/css/style.css>; rel=preload; as=style'
|
||||
implode(
|
||||
'; ',
|
||||
[
|
||||
'<' . $uri->getBasePath() . '/css/style.css>',
|
||||
'rel=preload', 'as=style'
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class LocaleMiddleware
|
|||
/**
|
||||
* Test if a locale can be used for the current user.
|
||||
*
|
||||
* @param mixed[] $proposedLocale Locale array created by AcceptLanguage::parse()
|
||||
* @param string[] $proposedLocale Locale array created by AcceptLanguage::parse()
|
||||
*
|
||||
* @return Locale|null Locale if chosen, nothing otherwise
|
||||
*/
|
||||
|
|
|
@ -31,7 +31,7 @@ class ReleaseCommand extends Tasks
|
|||
|
||||
$tmpDir = $this->_tmpDir();
|
||||
|
||||
$filename = 'alltube-' . trim((string)$result->getMessage()) . '.zip';
|
||||
$filename = 'alltube-' . trim($result->getMessage()) . '.zip';
|
||||
|
||||
/** @var FilesystemStack $rmTask */
|
||||
$rmTask = $this->taskFilesystemStack();
|
||||
|
|
|
@ -23,7 +23,7 @@ class ConvertedPlaylistArchiveStream extends PlaylistArchiveStream
|
|||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
protected function startVideoStream(Video $video)
|
||||
protected function startVideoStream(Video $video): void
|
||||
{
|
||||
$this->curVideoStream = new Stream($this->downloader->getAudioStream($video));
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function send($data)
|
||||
protected function send($data): void
|
||||
{
|
||||
$pos = $this->tell();
|
||||
|
||||
|
@ -133,7 +133,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
rewind($this->buffer);
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
fseek($this->buffer, $offset, $whence);
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
protected function startVideoStream(Video $video)
|
||||
protected function startVideoStream(Video $video): void
|
||||
{
|
||||
$response = $this->downloader->getHttpResponse($video);
|
||||
|
||||
|
@ -320,7 +320,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
public function close(): void
|
||||
{
|
||||
if (is_resource($this->buffer)) {
|
||||
fclose($this->buffer);
|
||||
|
|
|
@ -63,7 +63,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
public function close(): void
|
||||
{
|
||||
$this->response->getBody()->close();
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
$this->response->getBody()->seek($offset, $whence);
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->response->getBody()->rewind();
|
||||
}
|
||||
|
@ -156,9 +156,9 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @param mixed $string The string that is to be written
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function write($string)
|
||||
public function write($string): int
|
||||
{
|
||||
return $this->response->getBody()->write($string);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class UglyRouter extends Router
|
|||
*
|
||||
* @param ServerRequestInterface $request The current HTTP request object
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return int[]|string[]|array[]
|
||||
*
|
||||
* @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
|
||||
*/
|
||||
|
@ -55,7 +55,7 @@ class UglyRouter extends Router
|
|||
*/
|
||||
public function pathFor($name, array $data = [], array $queryParams = []): string
|
||||
{
|
||||
$queryParams['page'] = $name;
|
||||
$queryParams['page'] = $this->relativePathFor($name, $data);
|
||||
$url = Uri::createFromString($this->relativePathFor($name, $data, $queryParams))->withPath('');
|
||||
|
||||
if ($this->basePath) {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"name": "rudloff/alltube",
|
||||
"type": "project",
|
||||
"description": "HTML GUI for youtube-dl",
|
||||
"homepage": "http://alltubedownload.net/",
|
||||
"license": "GPL-3.0-only",
|
||||
"type": "project",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Pierre Rudloff",
|
||||
|
@ -14,7 +13,7 @@
|
|||
{
|
||||
"name": "Olivier Haquette",
|
||||
"email": "contact@olivierhaquette.fr",
|
||||
"homepage": "http://olivierhaquette.fr/",
|
||||
"homepage": "https://ographik.fr/",
|
||||
"role": "Designer"
|
||||
}
|
||||
],
|
||||
|
@ -29,22 +28,22 @@
|
|||
"j0k3r/httplug-ssrf-plugin": "^2.0",
|
||||
"jawira/case-converter": "^3.4",
|
||||
"jean85/pretty-package-versions": "^1.3",
|
||||
"mathmarques/smarty-view": "^1.1",
|
||||
"mathmarques/smarty-view": "^1.2",
|
||||
"oomphinc/composer-installers-extender": "^2.0",
|
||||
"paragonie/csp-builder": "^2.5",
|
||||
"rinvex/countries": "^6.1",
|
||||
"rudloff/alltube-library": "^0.1.1",
|
||||
"symfony/finder": "^5.0",
|
||||
"rudloff/alltube-library": "^0.1.3",
|
||||
"symfony/finder": "^5.4",
|
||||
"symfony/translation": "^4.0",
|
||||
"symfony/yaml": "^4.0",
|
||||
"webfontkit/open-sans": "^1.0",
|
||||
"ytdl-org/youtube-dl": "^2021.04",
|
||||
"ytdl-org/youtube-dl": "^2021.12",
|
||||
"zonuexe/http-accept-language": "^0.4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"consolidation/robo": "^2.1",
|
||||
"enlightn/security-checker": "^1.4",
|
||||
"ergebnis/composer-normalize": "^2.6",
|
||||
"ergebnis/composer-normalize": "^2.20",
|
||||
"insite/composer-dangling-locked-deps": "^0.2.1",
|
||||
"junker/debugbar-smarty": "^0.1.0",
|
||||
"kitchenu/slim-debugbar": "^1.1",
|
||||
|
@ -52,13 +51,45 @@
|
|||
"php-mock/php-mock-mockery": "^1.3",
|
||||
"phpro/grumphp": "^1.3",
|
||||
"phpstan/phpstan": "^0.12.72",
|
||||
"phpunit/phpunit": "^8.4",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"povils/phpmnd": "^2.5",
|
||||
"smarty-gettext/smarty-gettext": "^1.6",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/error-handler": "^5.0",
|
||||
"symfony/var-dumper": "^5.0"
|
||||
"symfony/error-handler": "^5.4",
|
||||
"symfony/var-dumper": "^5.4"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "ytdl-org/youtube-dl",
|
||||
"version": "2021.12.17",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://yt-dl.org/downloads/2021.12.17/youtube-dl-2021.12.17.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Alltube\\": "classes/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Alltube\\Test\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/installers": true,
|
||||
"cweagans/composer-patches": true,
|
||||
"ergebnis/composer-normalize": true,
|
||||
"insite/composer-dangling-locked-deps": true,
|
||||
"oomphinc/composer-installers-extender": true,
|
||||
"phpro/grumphp": true
|
||||
},
|
||||
"platform": {
|
||||
"php": "7.3.11"
|
||||
},
|
||||
|
@ -82,29 +113,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Alltube\\": "classes/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Alltube\\Test\\": "tests/"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "ytdl-org/youtube-dl",
|
||||
"version": "2021.04.01",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/ytdl-org/youtube-dl/archive/2021.04.01.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "grumphp run --ansi",
|
||||
"release": "robo release --ansi",
|
||||
|
|
2128
composer.lock
generated
2128
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -255,7 +255,7 @@ footer a:hover {
|
|||
margin-top: 12px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 622px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mp3-inner {
|
||||
|
@ -545,6 +545,7 @@ h1 {
|
|||
|
||||
.thumb {
|
||||
max-width: 700px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.format {
|
||||
|
|
|
@ -13,6 +13,7 @@ grumphp:
|
|||
securitychecker_enlightn: ~
|
||||
composer_normalize: ~
|
||||
composer_dangling_locked_deps: ~
|
||||
phpmnd: ~
|
||||
phpcs:
|
||||
standard: PSR12
|
||||
phpstan:
|
||||
|
|
|
@ -98,7 +98,7 @@ msgid "Share on Facebook"
|
|||
msgstr "شاركها على فيسبوك"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "انسخ هنا رابط الفيديو (يوتيوب، انستقرام، وغيرها)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
|
|
|
@ -131,8 +131,8 @@ msgid "Video password"
|
|||
msgstr "Videopasswort"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Kopiere hier die URL deines Videos (Youtube, Dailymotion, etc.) hinein"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Kopiere hier die URL deines Videos (YouTube, Dailymotion, etc.) hinein"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
|||
msgstr "Compartir en Facebook"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Copia aquí la URL de tu vídeo (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Copia aquí la URL de tu vídeo (YouTube, Dailymotion, etc.)"
|
||||
|
||||
#: templates/index.tpl:23
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -59,8 +59,8 @@ msgid "Video password"
|
|||
msgstr "Password del video"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Copia qui l'URL del video (Youtube, Dailymotion, ecc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Copia qui l'URL del video (YouTube, Dailymotion, ecc.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -134,7 +134,7 @@ msgstr "閲覧用パスワード"
|
|||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "動画のリンク(URL)を入力欄に入力してください。(例:Youtube,Dailymotion等。)"
|
||||
msgstr "動画のリンク(URL)を入力欄に入力してください。(例:YouTube,Dailymotion等。)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -71,8 +71,8 @@ msgid "Video password"
|
|||
msgstr "Hasło do wideo"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Zamieść link do wideo (Yotube, Dailymotion, itp.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Zamieść link do wideo (YouTube, Dailymotion, itp.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
|||
msgstr "Compartilhe no Facebook"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Cole aqui a URL do vídeo (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Cole aqui a URL do vídeo (YouTube, Dailymotion, etc.)"
|
||||
|
||||
#: templates/index.tpl:24
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -65,8 +65,8 @@ msgid "Video password"
|
|||
msgstr "Video parolası"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Videonuzun URL'sini buraya kopyalayın (Youtube, Dailymotion, vb.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Videonuzun URL'sini buraya kopyalayın (YouTube, Dailymotion, vb.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -1,134 +1,227 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: POEditor.com\n"
|
||||
"Project-Id-Version: AllTube Download\n"
|
||||
"Language: zh-CN\n"
|
||||
|
||||
#: templates/error.tpl:6
|
||||
msgid "Please check the URL of your video."
|
||||
msgstr "请检查您的视频的 URL。"
|
||||
|
||||
#: templates/playlist.tpl:5
|
||||
msgid "Videos extracted from"
|
||||
msgstr "视频提取自"
|
||||
|
||||
#: templates/playlist.tpl:7
|
||||
msgid ":"
|
||||
msgstr ":"
|
||||
|
||||
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83
|
||||
#: templates/video.tpl:86 templates/index.tpl:18
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: templates/playlist.tpl:27
|
||||
msgid "More options"
|
||||
msgstr "更多选项"
|
||||
|
||||
#: templates/password.tpl:5
|
||||
msgid "This video is protected"
|
||||
msgstr "这个视频受保护"
|
||||
|
||||
#: templates/password.tpl:6
|
||||
msgid "You need a password in order to download this video."
|
||||
msgstr "你需要密码才能下载这个视频。"
|
||||
|
||||
#: templates/password.tpl:8
|
||||
msgid "Video password"
|
||||
msgstr "视频密码"
|
||||
|
||||
#: templates/extractors.tpl:4
|
||||
msgid "Supported websites"
|
||||
msgstr "支持的网站"
|
||||
|
||||
#: templates/video.tpl:6
|
||||
msgid "You are going to download"
|
||||
msgstr "你即将下载"
|
||||
|
||||
#: templates/video.tpl:24
|
||||
msgid "Available formats:"
|
||||
msgstr "可用的格式︰"
|
||||
|
||||
#: templates/video.tpl:29
|
||||
msgid "Generic formats"
|
||||
msgstr "通用格式"
|
||||
|
||||
#: templates/video.tpl:32
|
||||
msgid "Best"
|
||||
msgstr "最佳"
|
||||
|
||||
#: templates/video.tpl:37
|
||||
msgid "Remux best video with best audio"
|
||||
msgstr "重新封装最佳视频与最佳音频"
|
||||
|
||||
#: templates/video.tpl:41
|
||||
msgid "Worst"
|
||||
msgstr "最差"
|
||||
|
||||
#: templates/video.tpl:44
|
||||
msgid "Detailed formats"
|
||||
msgstr "详细格式"
|
||||
|
||||
#: templates/inc/footer.tpl:4
|
||||
msgid "Code by"
|
||||
msgstr "代码来自"
|
||||
|
||||
#: templates/inc/footer.tpl:6
|
||||
msgid "Design by"
|
||||
msgstr "设计来自"
|
||||
|
||||
#: templates/inc/footer.tpl:12
|
||||
msgid "AllTube Download on Facebook"
|
||||
msgstr "去Alltube Download的Facebook页面"
|
||||
|
||||
#: templates/inc/footer.tpl:12
|
||||
msgid "Like us on Facebook"
|
||||
msgstr "在Facebook关注我们"
|
||||
|
||||
#: templates/inc/footer.tpl:14
|
||||
msgid "Get the code"
|
||||
msgstr "获取代码"
|
||||
#: templates/inc/footer.tpl:8
|
||||
msgid "Code by @dev"
|
||||
msgstr "由 @dev 开发"
|
||||
|
||||
#: templates/inc/footer.tpl:16
|
||||
msgid "Based on"
|
||||
msgstr "基于"
|
||||
msgid "Design by @designer"
|
||||
msgstr "由 @designer 设计"
|
||||
|
||||
#: templates/inc/header.tpl:21
|
||||
msgid "Share on Twitter"
|
||||
msgstr "分享到 Twitter"
|
||||
#: templates/inc/footer.tpl:21
|
||||
msgid "Get the code"
|
||||
msgstr "获取源代码"
|
||||
|
||||
#: templates/inc/header.tpl:23
|
||||
msgid "Share on Facebook"
|
||||
msgstr "分享到 Facebook"
|
||||
#: templates/inc/footer.tpl:29
|
||||
msgid "Based on @youtubedl"
|
||||
msgstr "基于 @youtubedl"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "在这里复制您的视频 (Youtube、 Dailymotion 等) 的 URL"
|
||||
#: templates/inc/footer.tpl:33
|
||||
msgid "Donate using Liberapay"
|
||||
msgstr "使用 Liberapay 捐赠"
|
||||
|
||||
#: templates/index.tpl:23
|
||||
msgid "Audio only (MP3)"
|
||||
msgstr "仅限音频(mp3)"
|
||||
|
||||
#: templates/index.tpl:28
|
||||
msgid "See all supported websites"
|
||||
msgstr "请参阅支持的所有网站"
|
||||
|
||||
#: templates/index.tpl:30
|
||||
msgid "Drag this to your bookmarks bar:"
|
||||
msgstr "把这个拖到你的书签:"
|
||||
|
||||
#: templates/index.tpl:31
|
||||
msgid "Bookmarklet"
|
||||
msgstr "书签工具"
|
||||
#: templates/inc/footer.tpl:35
|
||||
msgid "Donate"
|
||||
msgstr "捐赠"
|
||||
|
||||
#: templates/inc/header.tpl:4
|
||||
msgid "Switch language"
|
||||
msgstr "切换语言"
|
||||
|
||||
#: templates/inc/header.tpl:8
|
||||
msgid "Set language"
|
||||
msgstr "设置语言"
|
||||
|
||||
#: templates/info.tpl:11
|
||||
msgid "You are going to download @title."
|
||||
msgstr "您将要下载 @title。"
|
||||
|
||||
#: templates/info.tpl:29
|
||||
msgid "Available formats:"
|
||||
msgstr "可用的格式:"
|
||||
|
||||
#: templates/info.tpl:31
|
||||
msgid "Generic formats"
|
||||
msgstr "通用格式"
|
||||
|
||||
#: templates/info.tpl:35
|
||||
msgid "Best"
|
||||
msgstr "最佳"
|
||||
|
||||
#: templates/info.tpl:36
|
||||
msgid "Remux best video with best audio"
|
||||
msgstr "重新封装最佳视频和最佳音频"
|
||||
|
||||
#: templates/info.tpl:37
|
||||
msgid "Worst"
|
||||
msgstr "最差"
|
||||
|
||||
#: templates/info.tpl:42
|
||||
msgid "Detailed formats"
|
||||
msgstr "详细格式"
|
||||
|
||||
#: templates/info.tpl:86
|
||||
msgid "Stream the video through the server"
|
||||
msgstr "通过服务器传输视频"
|
||||
|
||||
#: templates/info.tpl:92
|
||||
msgid "Convert into a custom format:"
|
||||
msgstr "转换为自定义格式:"
|
||||
|
||||
#: templates/info.tpl:93
|
||||
msgid "Custom format"
|
||||
msgstr "自定义格式"
|
||||
|
||||
#: templates/info.tpl:93
|
||||
msgid "Format to convert to"
|
||||
msgstr "要转换到的格式"
|
||||
|
||||
#: templates/info.tpl:98
|
||||
# Other translators: Please check that file for context
|
||||
msgid "with"
|
||||
msgstr ",并带"
|
||||
|
||||
#: templates/info.tpl:99
|
||||
msgid "Bit rate"
|
||||
msgstr "比特率"
|
||||
|
||||
#: templates/info.tpl:100
|
||||
msgid "Custom bitrate"
|
||||
msgstr "自定义比特率"
|
||||
|
||||
#: templates/info.tpl:103
|
||||
msgid "kbit/s audio"
|
||||
msgstr "kbit/s 的音频"
|
||||
|
||||
#: templates/info.tpl:107 templates/playlist.tpl:38 templates/password.tpl:11
|
||||
#: templates/index.tpl:19
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: templates/playlist.tpl:12
|
||||
msgid "Videos extracted from @title:"
|
||||
msgstr "从 @title 中提取的视频:"
|
||||
|
||||
#: templates/playlist.tpl:39
|
||||
msgid "More options"
|
||||
msgstr "更多选项"
|
||||
|
||||
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:111
|
||||
msgid "Supported websites"
|
||||
msgstr "支持的网站"
|
||||
|
||||
#: templates/error.tpl:5
|
||||
msgid "An error occurred"
|
||||
msgstr "出错了"
|
||||
|
||||
#: templates/password.tpl:5
|
||||
msgid "This video is protected"
|
||||
msgstr "此视频受保护"
|
||||
|
||||
#: templates/password.tpl:6
|
||||
msgid "You need a password in order to download this video."
|
||||
msgstr "您需要密码才能下载此视频。"
|
||||
|
||||
#: templates/password.tpl:8
|
||||
msgid "Video password"
|
||||
msgstr "视频密码"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
# I don't think this needs to be a 100% match
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "在此处粘贴视频网址"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
msgstr "仅音频(MP3)"
|
||||
|
||||
#: templates/index.tpl:29
|
||||
# Still check that file for context
|
||||
msgid "From"
|
||||
msgstr "从"
|
||||
|
||||
#: templates/index.tpl:32
|
||||
msgid "to"
|
||||
msgstr "到"
|
||||
|
||||
#: templates/index.tpl:41
|
||||
msgid "See all supported websites"
|
||||
msgstr "查看所有支持的网站"
|
||||
|
||||
#: templates/index.tpl:43
|
||||
msgid "Drag this to your bookmarks bar:"
|
||||
msgstr "您可以把这个书签工具拖到您的书签栏中:"
|
||||
|
||||
#: templates/index.tpl:45
|
||||
msgid "Bookmarklet"
|
||||
msgstr "书签工具"
|
||||
|
||||
#: classes/Controller/DownloadController.php:64
|
||||
#: classes/Controller/FrontController.php:166
|
||||
msgid "Wrong password"
|
||||
msgstr "密码错误"
|
||||
|
||||
#: classes/Controller/DownloadController.php:69
|
||||
msgid "Conversion of playlists is not supported."
|
||||
msgstr "不支持转换播放列表。"
|
||||
|
||||
#: classes/Controller/DownloadController.php:76
|
||||
msgid "Conversion of M3U8 files is not supported."
|
||||
msgstr "不支持转换 M3U8 文件。"
|
||||
|
||||
#: classes/Controller/DownloadController.php:82
|
||||
# ref. Chinese Wikipedia article about DASH
|
||||
msgid "Conversion of DASH segments is not supported."
|
||||
msgstr "不支持转换 DASH 片段。"
|
||||
|
||||
#: classes/Controller/FrontController.php:65
|
||||
msgid ""
|
||||
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||
msgstr ""
|
||||
"轻松从 YouTube、Dailymotion、Vimeo 等网站下载视频。"
|
||||
|
||||
#: classes/Controller/FrontController.php:112
|
||||
# NOTE: DON'T translate AllTube Download
|
||||
msgid ""
|
||||
"List of all supported websites from which AllTube Download can extract video "
|
||||
"or audio files"
|
||||
msgstr ""
|
||||
"AllTube Download 能够提取视频"
|
||||
"或音频文件的的所有网站"
|
||||
|
||||
#: classes/Controller/FrontController.php:138
|
||||
msgid "Password prompt"
|
||||
msgstr "密码提示"
|
||||
|
||||
#: classes/Controller/FrontController.php:140
|
||||
msgid ""
|
||||
"You need a password in order to download this video with AllTube Download"
|
||||
msgstr ""
|
||||
"您需要密码才能使用 AllTube Download 下载此视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:174
|
||||
# Download page header?
|
||||
msgid "Video download"
|
||||
msgstr "下载视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:176
|
||||
msgid "Download video from @extractor"
|
||||
msgstr "从 @extractor 下载视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:182
|
||||
msgid "Download @title from @extractor"
|
||||
msgstr "从 @extractor 下载 @title"
|
||||
|
||||
#: classes/Controller/FrontController.php:255
|
||||
msgid "Error"
|
||||
msgstr "错误"
|
||||
|
||||
#: classes/Controller/FrontController.php:271
|
||||
msgid "Page not found"
|
||||
msgstr "找不到页面"
|
||||
|
||||
#: classes/Controller/FrontController.php:282
|
||||
msgid "Method not allowed"
|
||||
msgstr "不允许此请求方法"
|
||||
|
|
11
phpunit.xml
11
phpunit.xml
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<phpunit bootstrap="tests/bootstrap.php">
|
||||
<filter>
|
||||
<whitelist>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>tests/</directory>
|
||||
|
|
|
@ -6,19 +6,6 @@ Most recent browsers automatically play a video
|
|||
if it is a format they know how to play.
|
||||
You can usually download the video by doing *File > Save to* or *ctrl + S*.
|
||||
|
||||
## [alltubedownload.net](https://alltubedownload.net) is too slow
|
||||
|
||||
[alltubedownload.net](https://alltubedownload.net) is hosted on a free [Heroku server](https://www.heroku.com/pricing)
|
||||
so it has low RAM and CPU.
|
||||
|
||||
AllTube probably won't switch to a more expensive hosting
|
||||
because this project does not earn any financial resources
|
||||
and you are encouraged to host it yourself.
|
||||
|
||||
## alltubedownload.net often says "An error occurred in the application…"
|
||||
|
||||
See above.
|
||||
|
||||
## Change config parameters
|
||||
|
||||
You need to create a YAML file called `config.yml` in the `config/` folder.
|
||||
|
@ -68,8 +55,7 @@ There are two known workarounds:
|
|||
|
||||
* You can run AllTube locally on your computer.
|
||||
* You can enable streaming videos through the server (see below).
|
||||
Please note that this can use a lot of resources on the server
|
||||
(which is why we won't enable it on alltubedownload.net).
|
||||
Please note that this can use a lot of resources on the server.
|
||||
|
||||
## I get a 404 error on every page except the index
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
}
|
||||
],
|
||||
"lang": "en",
|
||||
"start_url": "./",
|
||||
"start_url": "../",
|
||||
"theme_color": "#4F4F4F",
|
||||
"background_color": "#EBEBEB",
|
||||
"orientation": "portrait"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://alltubedownload.net/</loc>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>1</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://alltubedownload.net/extractors</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>
|
||||
</urlset>
|
|
@ -1 +0,0 @@
|
|||
Sitemap: https://alltubedownload.net/resources/sitemap.xml
|
|
@ -1 +1 @@
|
|||
python-3.8.6
|
||||
python-3.8.12
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{include file='inc/head.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main error">
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div class="error">
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}An error occurred{/t}</h2>
|
||||
<p><i>{$error|escape|nl2br}</i></p>
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{include file='inc/head.tpl'}
|
||||
{include file='inc/header.tpl'}
|
||||
{include file='inc/logo.tpl'}
|
||||
<h2 class="titre">{t}Supported websites{/t}</h2>
|
||||
<div class="tripleliste">
|
||||
<ul>
|
||||
{foreach $extractors as $extractor}
|
||||
<li>{$extractor}</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
{include file='inc/footer.tpl'}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file='inc/logo.tpl'}
|
||||
<h2 class="titre">{t}Supported websites{/t}</h2>
|
||||
<div class="tripleliste">
|
||||
<ul>
|
||||
{foreach $extractors as $extractor}
|
||||
<li>{$extractor}</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
</div>
|
||||
<footer class="small-font">
|
||||
<div class="footer_wrapper">
|
||||
{$dev="<a rel='author' target='blank'
|
||||
href='http://rudloff.pro/'>
|
||||
Pierre Rudloff
|
||||
</a>"}
|
||||
{include file='snippets/dev.tpl' assign=dev}
|
||||
{t params=['@dev'=>$dev]}Code by @dev{/t}
|
||||
|
||||
·
|
||||
|
||||
{$designer="<a rel='author' target='blank'
|
||||
href='http://olivierhaquette.fr'>
|
||||
Olivier Haquette
|
||||
</a>"}
|
||||
{include file='snippets/designer.tpl' assign=designer}
|
||||
{t params=['@designer' => $designer]}Design by @designer{/t}
|
||||
|
||||
·
|
||||
|
@ -23,15 +16,7 @@
|
|||
|
||||
·
|
||||
|
||||
{$youtubedl="<a href='http://ytdl-org.github.io/youtube-dl/'>
|
||||
youtube-dl
|
||||
</a>"}
|
||||
{include file='snippets/youtubedl.tpl' assign=youtubedl}
|
||||
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{if isset($debug_render)}
|
||||
{$debug_render->render()}
|
||||
{/if}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
<!doctype html>
|
||||
<html lang="{$locale->getLocale()->getBcp47()}">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name=viewport content="width=device-width, initial-scale=1"/>
|
||||
|
@ -11,7 +9,6 @@
|
|||
<link rel="stylesheet" href="{base_url}/assets/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}"/>
|
||||
<link rel="icon" href="{base_url}/img/favicon.png"/>
|
||||
<meta property="og:title" content="{$config->appName}{if isset($title)} - {$title|escape}{/if}"/>
|
||||
<meta property="og:image" content="{base_url}/img/logo.png"/>
|
||||
|
@ -27,5 +24,3 @@
|
|||
{$debug_render->renderHead()}
|
||||
{/if}
|
||||
</head>
|
||||
<body>
|
||||
<div class="page {$class}">
|
||||
|
|
|
@ -27,4 +27,3 @@
|
|||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
<div class="wrapper">
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<h1 class="logobis">
|
||||
<a class="logocompatible" href="{path_for name="index"}">
|
||||
<span class="logocompatiblemask"><img src="{base_url}/img/logocompatiblemask.png" width="447" height="107"
|
||||
alt="{$config->appName}"/></span>
|
||||
</a></h1>
|
||||
<span class="logocompatiblemask">
|
||||
{html_image file='img/logocompatiblemask.png' path_prefix={base_url}|cat:'/' alt=$config->appName}
|
||||
</span>
|
||||
</a>
|
||||
</h1>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{include file='inc/head.tpl'}
|
||||
{include file='inc/header.tpl'}
|
||||
<main class="main">
|
||||
<div><img class="logo" src="{base_url}/img/logo.png"
|
||||
alt="{$config->appName}" width="328" height="284"></div>
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div>
|
||||
{html_image file='img/logo.png' path_prefix={base_url}|cat:'/' alt=$config->appName class="logo"}
|
||||
</div>
|
||||
<form action="{path_for name="info"}">
|
||||
<label class="labelurl" for="url">
|
||||
{t}Copy here the URL of your video (YouTube, Dailymotion, etc.){/t}
|
||||
|
@ -11,7 +11,7 @@
|
|||
<span class="URLinput_wrapper">
|
||||
<!-- We used to have an autofocus attribute on this field but it triggerd a very specific CSS bug: https://github.com/Rudloff/alltube/issues/117 -->
|
||||
<input class="URLinput large-font" type="url" name="url" id="url"
|
||||
required placeholder="http://example.com/video"/>
|
||||
required placeholder="https://example.com/video"/>
|
||||
</span>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="info"/>
|
||||
|
@ -20,18 +20,23 @@
|
|||
{if $config->convert}
|
||||
<div class="mp3 small-font">
|
||||
<div class="mp3-inner">
|
||||
<input type="checkbox" id="audio" class="audio" name="audio" {($config->defaultAudio) ? 'checked' : ''}>
|
||||
<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"
|
||||
id="from"/>
|
||||
<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 $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"
|
||||
id="from"/>
|
||||
<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>
|
||||
|
@ -44,6 +49,4 @@
|
|||
<a class="bookmarklet small-font"
|
||||
href="javascript:window.location='{$domain}{path_for name='info' queryParams=['url' => '%url%']}'.replace('%url%', encodeURIComponent(location.href));">{t}Bookmarklet{/t}</a>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
{/block}
|
||||
|
|
|
@ -1,111 +1,59 @@
|
|||
{include file="inc/head.tpl"}
|
||||
<div class="wrapper">
|
||||
<div itemscope itemtype="http://schema.org/VideoObject">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
{$title="<i itemprop='name'>
|
||||
<a itemprop='url' id='video_link'
|
||||
href='{$video->webpage_url}'>
|
||||
{$video->title}</a></i>"}
|
||||
<p id="download_intro">
|
||||
{t params=['@title' => $title]}You are going to download @title.{/t}
|
||||
</p>
|
||||
{if isset($video->thumbnail)}
|
||||
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt=""/>
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div itemscope itemtype="https://schema.org/VideoObject">
|
||||
{include file="inc/logo.tpl"}
|
||||
{include file='snippets/title.tpl' assign=title}
|
||||
<p id="download_intro">
|
||||
{t params=['@title' => $title]}You are going to download @title.{/t}
|
||||
</p>
|
||||
{if isset($video->thumbnail)}
|
||||
{html_image file=$video->thumbnail itemprop="thumbnailUrl" class="thumb"}
|
||||
{/if}
|
||||
{if isset($video->description)}
|
||||
<meta itemprop="description" content="{$video->description|escape}"/>
|
||||
{/if}
|
||||
{if isset($video->upload_date)}
|
||||
<meta itemprop="uploadDate" content="{$video->upload_date}"/>
|
||||
{/if}
|
||||
<br/>
|
||||
<form action="{path_for name="download"}">
|
||||
<input type="hidden" name="url" value="{$video->webpage_url}"/>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="download"/>
|
||||
{/if}
|
||||
{if isset($video->description)}
|
||||
<meta itemprop="description" content="{$video->description|escape}"/>
|
||||
{if isset($video->formats) && count($video->formats) > 1}
|
||||
<h3><label for="format">{t}Available formats:{/t}</label></h3>
|
||||
{*
|
||||
To make the default generic formats translatable:
|
||||
{t}Best{/t}
|
||||
{t}Remux best video with best audio{/t}
|
||||
{t}Worst{/t}
|
||||
*}
|
||||
{html_options name='format' options=$formats selected=$defaultFormat id="format" class="formats monospace"}
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if isset($video->upload_date)}
|
||||
<meta itemprop="uploadDate" content="{$video->upload_date}"/>
|
||||
{if $config->stream}
|
||||
<input type="checkbox" {if $config->stream !== 'ask'}checked{/if} name="stream" id="stream"/>
|
||||
<label for="stream">{t}Stream the video through the server{/t}</label>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<br/>
|
||||
<form action="{path_for name="download"}">
|
||||
<input type="hidden" name="url" value="{$video->webpage_url}"/>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="download"/>
|
||||
{/if}
|
||||
{if isset($video->formats) && count($video->formats) > 1}
|
||||
<h3><label for="format">{t}Available formats:{/t}</label></h3>
|
||||
<select name="format" id="format" class="formats monospace">
|
||||
<optgroup label="{t}Generic formats{/t}">
|
||||
{foreach $config->genericFormats as $format => $name}
|
||||
{*
|
||||
To make the default generic formats translatable:
|
||||
{t}Best{/t}
|
||||
{t}Remux best video with best audio{/t}
|
||||
{t}Worst{/t}
|
||||
*}
|
||||
<option value="{$format}">{t}{$name}{/t}</option>
|
||||
{/foreach}
|
||||
</optgroup>
|
||||
<optgroup label="{t}Detailed formats{/t}" class="monospace">
|
||||
{foreach $video->formats as $format}
|
||||
{if $config->stream || $format->protocol|in_array:array('http', 'https')}
|
||||
{strip}
|
||||
<option value="{$format->format_id}">
|
||||
{$format->ext}
|
||||
{for $foo=1 to (5 - ($format->ext|strlen))}
|
||||
|
||||
{/for}
|
||||
{if isset($format->width)}
|
||||
{$format->width}x{$format->height}
|
||||
{for $foo=1 to (10 - (("{$format->width}x{$format->height}")|strlen))}
|
||||
|
||||
{/for}
|
||||
{else}
|
||||
{for $foo=1 to 10}
|
||||
|
||||
{/for}
|
||||
{/if}
|
||||
{if isset($format->filesize)}
|
||||
{($format->filesize/1000000)|round:2} MB
|
||||
{for $foo=1 to (7 - (($format->filesize/1000000)|round:2|strlen))}
|
||||
|
||||
{/for}
|
||||
{else}
|
||||
{for $foo=1 to 10}
|
||||
|
||||
{/for}
|
||||
{/if}
|
||||
{if isset($format->format_note)}
|
||||
{$format->format_note}
|
||||
{/if}
|
||||
({$format->format_id})
|
||||
</option>
|
||||
{/strip}
|
||||
{/if}
|
||||
{/foreach}
|
||||
</optgroup>
|
||||
</select>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if $config->stream}
|
||||
<input type="checkbox" {if $config->stream !== 'ask'}checked{/if} name="stream" id="stream"/>
|
||||
<label for="stream">{t}Stream the video through the server{/t}</label>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if $config->convertAdvanced}
|
||||
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||
<select title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}">
|
||||
{foreach $config->convertAdvancedFormats as $format}
|
||||
<option>{$format}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
{t}with{/t}
|
||||
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
|
||||
<input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}"
|
||||
class="customBitrate"
|
||||
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit"/>
|
||||
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/><br/>
|
||||
</form>
|
||||
</main>
|
||||
{if $config->convertAdvanced}
|
||||
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||
{html_options name='customFormat' values=$config->convertAdvancedFormats output=$config->convertAdvancedFormats
|
||||
title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}"}
|
||||
{t}with{/t}
|
||||
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
|
||||
<input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}"
|
||||
class="customBitrate"
|
||||
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit"/>
|
||||
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/><br/>
|
||||
</form>
|
||||
</div>
|
||||
{include file="inc/footer.tpl"}
|
||||
{/block}
|
||||
|
|
18
templates/page.tpl
Normal file
18
templates/page.tpl
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html lang="{$locale->getLocale()->getBcp47()}">
|
||||
{include file='inc/head.tpl'}
|
||||
<body>
|
||||
<div class="page {$class}">
|
||||
{include file='inc/header.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{block name="main"}{/block}
|
||||
</main>
|
||||
</div>
|
||||
{include file='inc/footer.tpl'}
|
||||
</div>
|
||||
{if isset($debug_render)}
|
||||
{$debug_render->render()}
|
||||
{/if}
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +1,12 @@
|
|||
{include file='inc/head.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}This video is protected{/t}</h2>
|
||||
<p>{t}You need a password in order to download this video.{/t}</p>
|
||||
<form action="" method="POST">
|
||||
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
||||
<input class="URLinput" type="password" name="password" id="password"/>
|
||||
<br/><br/>
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
||||
</form>
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}This video is protected{/t}</h2>
|
||||
<p>{t}You need a password in order to download this video.{/t}</p>
|
||||
<form action="" method="POST">
|
||||
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
||||
<input class="URLinput" type="password" name="password" id="password"/>
|
||||
<br/><br/>
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
||||
</form>
|
||||
{/block}
|
||||
|
|
|
@ -1,44 +1,40 @@
|
|||
{include file="inc/head.tpl"}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file="inc/logo.tpl"}
|
||||
|
||||
{if isset($video->title)}
|
||||
{$title="<i>
|
||||
<a href='{$video->webpage_url}'>
|
||||
{$video->title}</a>
|
||||
</i>"}
|
||||
<p>
|
||||
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
||||
</p>
|
||||
{/if}
|
||||
{if isset($video->title)}
|
||||
{include file='snippets/title.tpl' assign=title}
|
||||
<p>
|
||||
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{if $config->stream}
|
||||
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
||||
{/if}
|
||||
{foreach $video->entries as $entry}
|
||||
<div class="playlist-entry">
|
||||
<h3 class="playlist-entry-title"><a target="_blank" href="{strip}
|
||||
{if $config->stream}
|
||||
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
||||
{/if}
|
||||
{foreach $video->entries as $entry}
|
||||
<div class="playlist-entry">
|
||||
<h3 class="playlist-entry-title">
|
||||
<a target="_blank" href="{strip}
|
||||
{if isset($entry->ie_key) and $entry->ie_key == Youtube and !filter_var($entry->url, FILTER_VALIDATE_URL)}
|
||||
https://www.youtube.com/watch?v=
|
||||
{/if}
|
||||
{$entry->url}
|
||||
{/strip}">
|
||||
{if !isset($entry->title)}
|
||||
{if $entry->ie_key == YoutubePlaylist}
|
||||
Playlist
|
||||
{else}
|
||||
Video
|
||||
{/if}
|
||||
{if !isset($entry->title)}
|
||||
{if $entry->ie_key == YoutubePlaylist}
|
||||
Playlist
|
||||
{else}
|
||||
{$entry->title}
|
||||
Video
|
||||
{/if}
|
||||
</a></h3>
|
||||
<a target="_blank" class="downloadBtn"
|
||||
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
||||
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
</main>
|
||||
{include file="inc/footer.tpl"}
|
||||
{else}
|
||||
{$entry->title}
|
||||
{/if}
|
||||
</a>
|
||||
</h3>
|
||||
<a target="_blank" class="downloadBtn"
|
||||
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
||||
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/block}
|
||||
|
|
4
templates/snippets/designer.tpl
Normal file
4
templates/snippets/designer.tpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
<a rel="author" target="blank"
|
||||
href="https://ographik.fr/">
|
||||
Olivier Haquette
|
||||
</a>
|
4
templates/snippets/dev.tpl
Normal file
4
templates/snippets/dev.tpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
<a rel="author" target="blank"
|
||||
href="https://rudloff.pro/">
|
||||
Pierre Rudloff
|
||||
</a>
|
5
templates/snippets/title.tpl
Normal file
5
templates/snippets/title.tpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i itemprop="name">
|
||||
<a itemprop="url" id="video_link"
|
||||
href="{$video->webpage_url}">
|
||||
{$video->title}</a>
|
||||
</i>
|
3
templates/snippets/youtubedl.tpl
Normal file
3
templates/snippets/youtubedl.tpl
Normal file
|
@ -0,0 +1,3 @@
|
|||
<a href="https://ytdl-org.github.io/youtube-dl/">
|
||||
youtube-dl
|
||||
</a>
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
namespace Alltube\Test;
|
||||
|
||||
use OndraM\CiDetector\CiDetector;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Util\Test;
|
||||
|
||||
/**
|
||||
* Abstract class used by every test.
|
||||
|
@ -37,7 +39,11 @@ abstract class BaseTest extends TestCase
|
|||
*/
|
||||
protected function checkRequirements()
|
||||
{
|
||||
$annotations = $this->getAnnotations();
|
||||
$ciDetector = new CiDetector();
|
||||
$annotations = Test::parseTestMethodAnnotations(
|
||||
static::class,
|
||||
$this->getName()
|
||||
);
|
||||
$requires = [];
|
||||
|
||||
if (isset($annotations['class']['requires'])) {
|
||||
|
@ -48,7 +54,7 @@ abstract class BaseTest extends TestCase
|
|||
}
|
||||
|
||||
foreach ($requires as $require) {
|
||||
if ($require == 'download' && getenv('CI')) {
|
||||
if ($require == 'download' && $ciDetector->isCiDetected()) {
|
||||
$this->markTestSkipped('Do not run tests that download videos on CI.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,14 +108,6 @@ class DownloadControllerTest extends ControllerTest
|
|||
public function testDownloadWithRtmpStream()
|
||||
{
|
||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||
|
||||
$config = $this->container->get('config');
|
||||
$config->setOptions(['stream' => true]);
|
||||
|
||||
$this->assertRequestIsOk(
|
||||
'download',
|
||||
['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +153,7 @@ class DownloadControllerTest extends ControllerTest
|
|||
*/
|
||||
public function testDownloadWithMissingPassword()
|
||||
{
|
||||
$this->assertRequestIsClientError('download', ['url' => 'http://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('download', ['url' => 'https://vimeo.com/68375962']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,7 +164,7 @@ class DownloadControllerTest extends ControllerTest
|
|||
public function testDownloadWithError()
|
||||
{
|
||||
$this->expectException(YoutubedlException::class);
|
||||
$this->getRequestResult('download', ['url' => 'http://example.com/foo']);
|
||||
$this->getRequestResult('download', ['url' => 'https://example.com/foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -186,12 +186,12 @@ class FrontControllerTest extends ControllerTest
|
|||
*
|
||||
* @return void
|
||||
* @requires download
|
||||
* @throws AlltubeLibraryException
|
||||
* @throws AlltubeLibraryException|InvalidURLException
|
||||
*/
|
||||
public function testInfoWithPassword()
|
||||
{
|
||||
$result = $this->controller->info(
|
||||
$this->container->get('request')->withQueryParams(['url' => 'http://vimeo.com/68375962'])
|
||||
$this->container->get('request')->withQueryParams(['url' => 'https://vimeo.com/68375962'])
|
||||
->withParsedBody(['password' => 'youtube-dl']),
|
||||
$this->container->get('response')
|
||||
);
|
||||
|
@ -206,8 +206,8 @@ class FrontControllerTest extends ControllerTest
|
|||
*/
|
||||
public function testInfoWithMissingPassword()
|
||||
{
|
||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962', 'audio' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,7 +49,7 @@ class JsonControllerTest extends ControllerTest
|
|||
public function testJsonWithError()
|
||||
{
|
||||
$this->expectException(YoutubedlException::class);
|
||||
$this->getRequestResult('json', ['url' => 'http://example.com/foo']);
|
||||
$this->getRequestResult('json', ['url' => 'https://example.com/foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -79,7 +79,7 @@ class UglyRouterTest extends ContainerTest
|
|||
public function testPathFor()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'/?page=foo',
|
||||
'/?page=%2Ffoo',
|
||||
$this->router->pathFor('foo', [], [])
|
||||
);
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class UglyRouterTest extends ContainerTest
|
|||
{
|
||||
$this->router->setBasePath('/bar');
|
||||
$this->assertEquals(
|
||||
'/bar/?page=foo',
|
||||
'/bar/?page=%2Ffoo',
|
||||
$this->router->pathFor('foo', [], [])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ class VideoTest extends ContainerTest
|
|||
*/
|
||||
public function testgetUrlWithPassword()
|
||||
{
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'youtube-dl');
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', 'best', 'youtube-dl');
|
||||
foreach ($video->getUrl() as $videoURL) {
|
||||
$this->assertStringContainsString('vimeocdn.com', $videoURL);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class VideoTest extends ContainerTest
|
|||
public function testgetUrlWithMissingPassword()
|
||||
{
|
||||
$this->expectException(PasswordException::class);
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', $this->format);
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', $this->format);
|
||||
$video->getUrl();
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ class VideoTest extends ContainerTest
|
|||
public function testgetUrlWithWrongPassword()
|
||||
{
|
||||
$this->expectException(WrongPasswordException::class);
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'foo');
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', 'best', 'foo');
|
||||
$video->getUrl();
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ class VideoTest extends ContainerTest
|
|||
'googlevideo.com',
|
||||
],
|
||||
[
|
||||
'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
||||
'https://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
||||
'Kaleidoscope_Leonard_Cohen-b039d07m',
|
||||
'flv',
|
||||
'bbcodspdns.fcod.llnwd.net',
|
||||
|
@ -247,7 +247,7 @@ class VideoTest extends ContainerTest
|
|||
public function errorUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
['http://example.com/video'],
|
||||
['https://example.com/video'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -479,16 +479,11 @@ class VideoTest extends ContainerTest
|
|||
* @param string $format Format
|
||||
*
|
||||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
* @dataProvider rtmpUrlProvider
|
||||
*/
|
||||
public function testGetRtmpStream(string $url, string $format)
|
||||
{
|
||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||
|
||||
$video = new Video($this->downloader, $url, $format);
|
||||
|
||||
$this->assertStream($this->downloader->getRtmpStream($video));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue