mirror of https://github.com/Rudloff/alltube.git
Compare commits
131 Commits
3.0.0-beta
...
master
Author | SHA1 | Date |
---|---|---|
Pierre Rudloff | ec95a8f1b7 | |
Pierre Rudloff | a9da2314af | |
dependabot[bot] | fcb3d2e84c | |
Pierre Rudloff | f09b7b43d7 | |
dependabot[bot] | a4146a63c8 | |
Pierre Rudloff | 3e45f19682 | |
Pierre Rudloff | 9564764633 | |
Pierre Rudloff | 71647158d3 | |
Pierre Rudloff | b23ce88be8 | |
Pierre Rudloff | b52a582539 | |
Pierre Rudloff | 7bfe55fff6 | |
Pierre Rudloff | 9d8bff3c42 | |
Pierre Rudloff | 2bef4d551d | |
Pierre Rudloff | f475fa2a47 | |
Pierre Rudloff | 8e6e88a2b2 | |
Pierre Rudloff | 2d60fd32ef | |
Pierre Rudloff | f32412e861 | |
Samuel Tan | d060650833 | |
Pierre Rudloff | 4e09393fd9 | |
Pierre Rudloff | 5d5a6624b8 | |
Pierre Rudloff | 36a91c8d4d | |
Pierre Rudloff | 1031ad152d | |
Pierre Rudloff | 87e30f2e87 | |
dependabot[bot] | 3b6b1f0387 | |
Pierre Rudloff | b95fed4935 | |
Pierre Rudloff | b5f757b562 | |
Pierre Rudloff | ffeda5ea90 | |
Pierre Rudloff | e9efc6ef71 | |
dependabot[bot] | 550371db7c | |
dependabot[bot] | 4e826e554d | |
Pierre Rudloff | 1055ce0c4b | |
Pierre Rudloff | 57dd9a7dd3 | |
Pierre Rudloff | e53393d670 | |
Pierre Rudloff | f6ae6eded3 | |
dependabot[bot] | e7fd4c6bc4 | |
dependabot[bot] | b894cdd6ce | |
Pierre Rudloff | e81b8c75a8 | |
Pierre Rudloff | e3187a7258 | |
Liu Wenyuan | 1d1e804b71 | |
dependabot[bot] | 6731fcdf96 | |
Pierre Rudloff | 10b7658240 | |
Pierre Rudloff | 3d09289104 | |
Pierre Rudloff | 8913f27716 | |
Pierre Rudloff | 113b3d5e50 | |
Pierre Rudloff | edaf6f82c0 | |
Pierre Rudloff | f814ebc492 | |
Pierre Rudloff | dad8b6d704 | |
Pierre Rudloff | 363bf9b08c | |
Pierre Rudloff | 732baccd63 | |
Pierre Rudloff | 7f28275fb0 | |
Pierre Rudloff | 148a171b24 | |
Pierre Rudloff | 1b099bb983 | |
Pierre Rudloff | 3a4f09dda0 | |
Pierre Rudloff | bf4a761d3a | |
Pierre Rudloff | 6ad0486468 | |
Pierre Rudloff | e246ab03e9 | |
Pierre Rudloff | e567f9c9fa | |
Pierre Rudloff | 64ac180a53 | |
Pierre Rudloff | 2afbfb4bf2 | |
Pierre Rudloff | 9410d4b49b | |
Pierre Rudloff | bfaea0e381 | |
Pierre Rudloff | 3ab22c654a | |
Pierre Rudloff | bc14b6e45c | |
Pierre Rudloff | acbd2008ca | |
Pierre Rudloff | cf82f1cc8f | |
Pierre Rudloff | 5677ce719a | |
Pierre Rudloff | 655490eeb3 | |
Pierre Rudloff | 18847e4d75 | |
Pierre Rudloff | fe771886d9 | |
Pierre Rudloff | 27439c7e14 | |
Pierre Rudloff | d9ba01f017 | |
Pierre Rudloff | ce9b4d9a48 | |
Pierre Rudloff | 7cd42e6c6b | |
Pierre Rudloff | ac8c53375a | |
Pierre Rudloff | de74808459 | |
Pierre Rudloff | bdf5554430 | |
Pierre Rudloff | b8c88aecf5 | |
Pierre Rudloff | d46563f994 | |
Pierre Rudloff | 781b5c8bc2 | |
Pierre Rudloff | ffd9275500 | |
Pierre Rudloff | 6fef87f58b | |
Pierre Rudloff | 835170f4b5 | |
Pierre Rudloff | 5ed15afe1f | |
Pierre Rudloff | 359c358df1 | |
Pierre Rudloff | c44979bbae | |
Pierre Rudloff | 8f3f1cdaf8 | |
ShinProg (Logan Tann) | 1464b2c319 | |
dependabot[bot] | fb78ecb410 | |
Pierre Rudloff | d744ee557e | |
Pierre Rudloff | 5d40523cf4 | |
Pierre Rudloff | 55db198d39 | |
M*C*O | 60f924f4bf | |
Pierre Rudloff | 607efaa292 | |
Pierre Rudloff | f3ffa90a2e | |
Pierre Rudloff | a95d1de67e | |
Pierre Rudloff | 1753adf478 | |
Pierre Rudloff | eeda434b2f | |
Pierre Rudloff | b902c9027b | |
Advizormcpe1 | be3f7d9a82 | |
Pierre Rudloff | 97d6532388 | |
dependabot[bot] | 6ab19b6d84 | |
Pierre Rudloff | 73e4fc1b13 | |
Pierre Rudloff | 9688244285 | |
Pierre Rudloff | 104a866188 | |
Pierre Rudloff | 3cfd450258 | |
Pierre Rudloff | 1e17dff21e | |
Pierre Rudloff | f2be3a7e5b | |
Pierre Rudloff | 9a27e7764a | |
Pierre Rudloff | 36ba147430 | |
Pierre Rudloff | 50fe879f16 | |
Pierre Rudloff | 9af922f3f1 | |
Pierre Rudloff | bba5090ec3 | |
Pierre Rudloff | 5c0ed594f3 | |
Pierre Rudloff | 58f79c5012 | |
Pierre Rudloff | 5d550a100d | |
Pierre Rudloff | 077bb4762e | |
prog-it | 03d6a59b26 | |
Pierre Rudloff | 05311ac7b6 | |
Pierre Rudloff | f2785bca03 | |
Pierre Rudloff | 9921b6210f | |
Pierre Rudloff | c9aa41d206 | |
Éric Gaspar | a5bda1d35e | |
Pierre Rudloff | f2bef49ad6 | |
Pierre Rudloff | f184bda59b | |
Pierre Rudloff | 05959b17f0 | |
Pierre Rudloff | b5a585443a | |
Pierre Rudloff | 81e42057f9 | |
Pierre Rudloff | a800a058fa | |
Pierre Rudloff | 2d1d69a1f1 | |
Tripp Sanders | bd2b72721f | |
Pierre Rudloff | c844d3bf74 |
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
name: New issue
|
||||||
|
description: Please answer these questions when reporting a new issue.
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What is your operating system (Windows, Linux, OSX, etc.)?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What is your web server (Apache, IIS, etc.)?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What version of AllTube are you using?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: How did you install AllTube?
|
||||||
|
options:
|
||||||
|
- Git
|
||||||
|
- Release package
|
||||||
|
- Docker
|
||||||
|
- Other (please specify)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What version of PHP are you using?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What version of Python are you using?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What version of youtube-dl are you using?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Do you get any PHP-related errors in your webserver's logs?
|
||||||
|
description: If so, paste them here.
|
||||||
|
render: plain text
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: What is the content of your "config/config.yml" file?
|
||||||
|
description: If you don't have this file, you can ignore this question.
|
||||||
|
render: yml
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Please provide the URL of a video that causes the issue.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe your issue
|
||||||
|
validations:
|
||||||
|
required: true
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
name: Tests
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-version:
|
||||||
|
- '7.4'
|
||||||
|
- '8.0'
|
||||||
|
- '8.1'
|
||||||
|
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>
|
<ifmodule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
|
|
||||||
RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC]
|
|
||||||
RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L]
|
|
||||||
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteRule ^ index.php [QSA,L]
|
RewriteRule ^ index.php [QSA,L]
|
||||||
</ifmodule>
|
</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,7 +1,6 @@
|
||||||
FROM php:7.3-apache
|
FROM php:7.4-apache
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get install -y libicu-dev xz-utils git python libgmp-dev unzip ffmpeg
|
RUN apt-get install -y libicu-dev xz-utils git python libgmp-dev unzip ffmpeg
|
||||||
RUN docker-php-ext-install mbstring
|
|
||||||
RUN docker-php-ext-install intl
|
RUN docker-php-ext-install intl
|
||||||
RUN docker-php-ext-install gmp
|
RUN docker-php-ext-install gmp
|
||||||
RUN a2enmod rewrite
|
RUN a2enmod rewrite
|
||||||
|
@ -10,4 +9,7 @@ COPY resources/php.ini /usr/local/etc/php/
|
||||||
COPY . /var/www/html/
|
COPY . /var/www/html/
|
||||||
RUN php composer.phar check-platform-reqs --no-dev
|
RUN php composer.phar check-platform-reqs --no-dev
|
||||||
RUN php composer.phar install --prefer-dist --no-progress --no-dev --optimize-autoloader
|
RUN php composer.phar install --prefer-dist --no-progress --no-dev --optimize-autoloader
|
||||||
|
RUN mkdir /var/www/html/templates_c/
|
||||||
|
RUN chmod 770 -R /var/www/html/templates_c/
|
||||||
|
RUN chown www-data -R /var/www/html/templates_c/
|
||||||
ENV CONVERT=1
|
ENV CONVERT=1
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
# New issue
|
|
||||||
|
|
||||||
## Your environment
|
|
||||||
|
|
||||||
Please answer these questions when reporting a new issue:
|
|
||||||
|
|
||||||
**What is your operating system (Windows, Linux, OSX, etc.)?**
|
|
||||||
|
|
||||||
**What is your web server (Apache, IIS, etc.)?**
|
|
||||||
|
|
||||||
**What version of AllTube are you using?**
|
|
||||||
|
|
||||||
**How did you install AllTube (with Git or with a release package)?**
|
|
||||||
|
|
||||||
**What version of PHP are you using?**
|
|
||||||
|
|
||||||
**What version of Python are you using?**
|
|
||||||
|
|
||||||
**What version of youtube-dl are you using?**
|
|
||||||
|
|
||||||
**Do you get any PHP-related errors in your webserver's logs?**
|
|
||||||
|
|
||||||
**What is the content of your `config/config.yml` file?**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Insert content here.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Please provide the URL of a video that causes the issue.**
|
|
||||||
|
|
||||||
## Describe your issue
|
|
|
@ -1,8 +1,6 @@
|
||||||
# AllTube Download
|
# AllTube Download
|
||||||
|
|
||||||
[![Donate using Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Rudloff/donate)
|
HTML GUI for youtube-dl
|
||||||
|
|
||||||
HTML GUI for youtube-dl ([alltubedownload.net](http://alltubedownload.net/))
|
|
||||||
|
|
||||||
![Screenshot](img/screenshot.png "AllTube GUI screenshot")
|
![Screenshot](img/screenshot.png "AllTube GUI screenshot")
|
||||||
|
|
||||||
|
@ -81,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-Host (ex. `another.domain.com`)
|
||||||
* X-Forwarded-Path (ex: `/alltube`)
|
* X-Forwarded-Path (ex: `/alltube`)
|
||||||
* X-Forwarded-Port (ex: `5555`)
|
* X-Forwarded-Port (ex: `5555`)
|
||||||
|
* X-Forwarded-Proto (ex: `https`)
|
||||||
|
|
||||||
### Apache
|
### Apache
|
||||||
|
|
||||||
|
@ -166,7 +165,7 @@ so that you can reuse it in your projects.
|
||||||
## JSON API
|
## JSON API
|
||||||
|
|
||||||
We also provide a JSON API that you can use like this:
|
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.
|
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).
|
You can find a list of all the properties [in the youtube-dl documentation](https://github.com/ytdl-org/youtube-dl#output-template).
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Only the latest major release is supported.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
|---------|-----------|
|
||||||
|
| 3.x | Yes |
|
||||||
|
| 2.x | No |
|
||||||
|
| 1.x | No |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you need to report a vulnerability, you can send an e-mail to
|
||||||
|
[contact@rudloff.pro](mailto:contact@rudloff.pro).
|
4
app.json
4
app.json
|
@ -2,7 +2,6 @@
|
||||||
"name": "AllTube Download",
|
"name": "AllTube Download",
|
||||||
"description": "HTML GUI for youtube-dl",
|
"description": "HTML GUI for youtube-dl",
|
||||||
"repository": "https://github.com/Rudloff/alltube.git",
|
"repository": "https://github.com/Rudloff/alltube.git",
|
||||||
"logo": "https://alltubedownload.net/img/logo.png",
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"alltube",
|
"alltube",
|
||||||
"download",
|
"download",
|
||||||
|
@ -28,6 +27,5 @@
|
||||||
"value": "false",
|
"value": "false",
|
||||||
"required": false
|
"required": false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"website": "https://alltubedownload.net/"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Alltube\Controller\JsonController;
|
||||||
use Alltube\Exception\ConfigException;
|
use Alltube\Exception\ConfigException;
|
||||||
use Alltube\Exception\DependencyException;
|
use Alltube\Exception\DependencyException;
|
||||||
use Alltube\Factory\ConfigFactory;
|
use Alltube\Factory\ConfigFactory;
|
||||||
|
use Alltube\Factory\DebugBarFactory;
|
||||||
use Alltube\Factory\LocaleManagerFactory;
|
use Alltube\Factory\LocaleManagerFactory;
|
||||||
use Alltube\Factory\LoggerFactory;
|
use Alltube\Factory\LoggerFactory;
|
||||||
use Alltube\Factory\SessionFactory;
|
use Alltube\Factory\SessionFactory;
|
||||||
|
@ -16,6 +17,7 @@ use Alltube\Middleware\CspMiddleware;
|
||||||
use Alltube\Middleware\LinkHeaderMiddleware;
|
use Alltube\Middleware\LinkHeaderMiddleware;
|
||||||
use Alltube\Middleware\LocaleMiddleware;
|
use Alltube\Middleware\LocaleMiddleware;
|
||||||
use Alltube\Middleware\RouterPathMiddleware;
|
use Alltube\Middleware\RouterPathMiddleware;
|
||||||
|
use DebugBar\DebugBarException;
|
||||||
use Slim\Container;
|
use Slim\Container;
|
||||||
use SmartyException;
|
use SmartyException;
|
||||||
|
|
||||||
|
@ -26,6 +28,7 @@ class App extends \Slim\App
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
* @throws DependencyException
|
* @throws DependencyException
|
||||||
* @throws SmartyException
|
* @throws SmartyException
|
||||||
|
* @throws DebugBarException
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
@ -34,6 +37,8 @@ class App extends \Slim\App
|
||||||
/** @var Container $container */
|
/** @var Container $container */
|
||||||
$container = $this->getContainer();
|
$container = $this->getContainer();
|
||||||
|
|
||||||
|
$container['root_path'] = $this->getRootPath();
|
||||||
|
|
||||||
// Config.
|
// Config.
|
||||||
$container['config'] = ConfigFactory::create($container);
|
$container['config'] = ConfigFactory::create($container);
|
||||||
|
|
||||||
|
@ -43,16 +48,21 @@ class App extends \Slim\App
|
||||||
// Locales.
|
// Locales.
|
||||||
$container['locale'] = LocaleManagerFactory::create($container);
|
$container['locale'] = LocaleManagerFactory::create($container);
|
||||||
|
|
||||||
// Smarty.
|
|
||||||
$container['view'] = ViewFactory::create($container);
|
|
||||||
|
|
||||||
// Logger.
|
// Logger.
|
||||||
$container['logger'] = LoggerFactory::create($container);
|
$container['logger'] = LoggerFactory::create($container);
|
||||||
|
|
||||||
|
if ($container->get('config')->debug) {
|
||||||
|
// Debug bar.
|
||||||
|
$container['debugbar'] = DebugBarFactory::create($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smarty.
|
||||||
|
$container['view'] = ViewFactory::create($container);
|
||||||
|
|
||||||
// Middlewares.
|
// Middlewares.
|
||||||
$this->add(new LocaleMiddleware($container));
|
$this->add(new LocaleMiddleware($container));
|
||||||
$this->add(new CspMiddleware($container));
|
$this->add(new CspMiddleware($container));
|
||||||
$this->add(new LinkHeaderMiddleware($container));
|
$this->add(new LinkHeaderMiddleware());
|
||||||
$this->add(new RouterPathMiddleware($container));
|
$this->add(new RouterPathMiddleware($container));
|
||||||
|
|
||||||
// Controllers.
|
// Controllers.
|
||||||
|
@ -84,7 +94,7 @@ class App extends \Slim\App
|
||||||
|
|
||||||
$this->any(
|
$this->any(
|
||||||
'/watch',
|
'/watch',
|
||||||
[$frontController, 'info']
|
[$frontController, 'watch']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->any(
|
$this->any(
|
||||||
|
@ -102,4 +112,17 @@ class App extends \Slim\App
|
||||||
[$jsonController, 'json']
|
[$jsonController, 'json']
|
||||||
)->setName('json');
|
)->setName('json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function getRootPath(): ?string
|
||||||
|
{
|
||||||
|
// realpath() can return false but we prefer using null.
|
||||||
|
if ($rootPath = realpath(__DIR__ . '/../')) {
|
||||||
|
return $rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,90 +19,97 @@ use Jawira\CaseConverter\Convert;
|
||||||
*/
|
*/
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* youtube-dl binary path.
|
* youtube-dl binary path.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $youtubedl = 'vendor/ytdl-org/youtube-dl/youtube_dl/__main__.py';
|
public string $youtubedl = 'vendor/yt-dlp/yt-dlp/yt_dlp/__main__.py';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* python binary path.
|
* python binary path.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $python = '/usr/bin/python';
|
public string $python = '/usr/bin/python';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* youtube-dl parameters.
|
* youtube-dl parameters.
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
public $params = ['--no-warnings', '--ignore-errors', '--flat-playlist', '--restrict-filenames', '--no-playlist'];
|
public array $params = [
|
||||||
|
'--no-warnings',
|
||||||
|
'--ignore-errors',
|
||||||
|
'--flat-playlist',
|
||||||
|
'--restrict-filenames',
|
||||||
|
'--no-playlist',
|
||||||
|
'--use-extractors',
|
||||||
|
'default,-generic',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable audio conversion.
|
* Enable audio conversion.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $convert = false;
|
public bool $convert = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable advanced conversion mode.
|
* Enable advanced conversion mode.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $convertAdvanced = false;
|
public bool $convertAdvanced = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of formats available in advanced conversion mode.
|
* List of formats available in advanced conversion mode.
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
public $convertAdvancedFormats = ['mp3', 'avi', 'flv', 'wav'];
|
public array $convertAdvancedFormats = ['mp3', 'avi', 'flv', 'wav'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ffmpeg binary path.
|
* ffmpeg binary path.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $ffmpeg = '/usr/bin/ffmpeg';
|
public string $ffmpeg = '/usr/bin/ffmpeg';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path to the directory that contains the phantomjs binary.
|
* Path to the directory that contains the phantomjs binary.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $phantomjsDir = '/usr/bin/';
|
public string $phantomjsDir = '/usr/bin/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable URL rewriting.
|
* Disable URL rewriting.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $uglyUrls = false;
|
public bool $uglyUrls = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream downloaded files trough server?
|
* Stream downloaded files trough server?
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $stream = false;
|
public bool $stream = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow to remux video + audio?
|
* Allow to remux video + audio?
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $remux = false;
|
public bool $remux = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MP3 bitrate when converting (in kbit/s).
|
* MP3 bitrate when converting (in kbit/s).
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $audioBitrate = 128;
|
public int $audioBitrate = 128;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ffmpeg logging level.
|
* ffmpeg logging level.
|
||||||
|
@ -110,21 +117,21 @@ class Config
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $ffmpegVerbosity = 'error';
|
public string $ffmpegVerbosity = 'error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App name.
|
* App name.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $appName = 'AllTube Download';
|
public string $appName = 'AllTube Download';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic formats supported by youtube-dl.
|
* Generic formats supported by youtube-dl.
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
public $genericFormats = [
|
public array $genericFormats = [
|
||||||
'best/bestvideo' => 'Best',
|
'best/bestvideo' => 'Best',
|
||||||
'bestvideo+bestaudio' => 'Remux best video with best audio',
|
'bestvideo+bestaudio' => 'Remux best video with best audio',
|
||||||
'worst/worstvideo' => 'Worst',
|
'worst/worstvideo' => 'Worst',
|
||||||
|
@ -135,26 +142,26 @@ class Config
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $debug = false;
|
public bool $debug = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default to audio.
|
* Default to audio.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $defaultAudio = false;
|
public bool $defaultAudio = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable audio conversion from/to seeker.
|
* Disable audio conversion from/to seeker.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $convertSeek = true;
|
public bool $convertSeek = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config constructor.
|
* Config constructor.
|
||||||
*
|
*
|
||||||
* @param mixed[] $options Options
|
* @param scalar[]|scalar[][]|null[] $options Options
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
|
@ -187,7 +194,7 @@ class Config
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function addHttpToFormat(string $format)
|
public static function addHttpToFormat(string $format): string
|
||||||
{
|
{
|
||||||
$newFormat = [];
|
$newFormat = [];
|
||||||
foreach (explode('/', $format) as $subformat) {
|
foreach (explode('/', $format) as $subformat) {
|
||||||
|
@ -205,7 +212,7 @@ class Config
|
||||||
* @throws ConfigException If Python is missing
|
* @throws ConfigException If Python is missing
|
||||||
* @throws ConfigException If youtube-dl is missing
|
* @throws ConfigException If youtube-dl is missing
|
||||||
*/
|
*/
|
||||||
private function validateOptions()
|
private function validateOptions(): void
|
||||||
{
|
{
|
||||||
if (!is_file($this->youtubedl)) {
|
if (!is_file($this->youtubedl)) {
|
||||||
throw new ConfigException("Can't find youtube-dl at " . $this->youtubedl);
|
throw new ConfigException("Can't find youtube-dl at " . $this->youtubedl);
|
||||||
|
@ -222,11 +229,11 @@ class Config
|
||||||
/**
|
/**
|
||||||
* Apply the provided options.
|
* Apply the provided options.
|
||||||
*
|
*
|
||||||
* @param mixed[] $options Options
|
* @param scalar[]|scalar[][]|null[] $options Options
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function applyOptions(array $options)
|
private function applyOptions(array $options): void
|
||||||
{
|
{
|
||||||
foreach ($options as $option => $value) {
|
foreach ($options as $option => $value) {
|
||||||
if (isset($this->$option) && isset($value)) {
|
if (isset($this->$option) && isset($value)) {
|
||||||
|
@ -243,7 +250,7 @@ class Config
|
||||||
* @return void
|
* @return void
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
private function getEnv()
|
private function getEnv(): void
|
||||||
{
|
{
|
||||||
foreach (get_object_vars($this) as $prop => $value) {
|
foreach (get_object_vars($this) as $prop => $value) {
|
||||||
try {
|
try {
|
||||||
|
@ -266,7 +273,7 @@ class Config
|
||||||
* @return Config
|
* @return Config
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public static function fromFile(string $file)
|
public static function fromFile(string $file): Config
|
||||||
{
|
{
|
||||||
if (is_file($file)) {
|
if (is_file($file)) {
|
||||||
return new self(Yaml::parse(strval(file_get_contents($file))));
|
return new self(Yaml::parse(strval(file_get_contents($file))));
|
||||||
|
@ -278,11 +285,11 @@ class Config
|
||||||
/**
|
/**
|
||||||
* Manually set some options.
|
* 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
|
* @return void
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public function setOptions(array $options)
|
public function setOptions(array $options): void
|
||||||
{
|
{
|
||||||
$this->applyOptions($options);
|
$this->applyOptions($options);
|
||||||
$this->validateOptions();
|
$this->validateOptions();
|
||||||
|
@ -293,7 +300,7 @@ class Config
|
||||||
*
|
*
|
||||||
* @return Downloader
|
* @return Downloader
|
||||||
*/
|
*/
|
||||||
public function getDownloader()
|
public function getDownloader(): Downloader
|
||||||
{
|
{
|
||||||
return new Downloader(
|
return new Downloader(
|
||||||
$this->youtubedl,
|
$this->youtubedl,
|
||||||
|
@ -308,7 +315,7 @@ class Config
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getAppVersion()
|
public function getAppVersion(): string
|
||||||
{
|
{
|
||||||
$version = PrettyVersions::getRootPackageVersion();
|
$version = PrettyVersions::getRootPackageVersion();
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,11 @@ use Alltube\Library\Downloader;
|
||||||
use Alltube\Library\Video;
|
use Alltube\Library\Video;
|
||||||
use Alltube\LocaleManager;
|
use Alltube\LocaleManager;
|
||||||
use Aura\Session\Segment;
|
use Aura\Session\Segment;
|
||||||
use Consolidation\Log\Logger;
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Options;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Url;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
use Slim\Router;
|
use Slim\Router;
|
||||||
|
@ -27,21 +30,21 @@ abstract class BaseController
|
||||||
*
|
*
|
||||||
* @var Video
|
* @var Video
|
||||||
*/
|
*/
|
||||||
protected $video;
|
protected Video $video;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default youtube-dl format.
|
* Default youtube-dl format.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $defaultFormat = 'best/bestvideo';
|
protected string $defaultFormat = 'best/bestvideo';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slim dependency container.
|
* Slim dependency container.
|
||||||
*
|
*
|
||||||
* @var ContainerInterface
|
* @var ContainerInterface
|
||||||
*/
|
*/
|
||||||
protected $container;
|
protected ContainerInterface $container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config instance.
|
* Config instance.
|
||||||
|
@ -72,7 +75,7 @@ abstract class BaseController
|
||||||
protected $downloader;
|
protected $downloader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Logger
|
* @var LoggerInterface
|
||||||
*/
|
*/
|
||||||
protected $logger;
|
protected $logger;
|
||||||
|
|
||||||
|
@ -111,7 +114,7 @@ abstract class BaseController
|
||||||
*
|
*
|
||||||
* @return string format
|
* @return string format
|
||||||
*/
|
*/
|
||||||
protected function getFormat(Request $request)
|
protected function getFormat(Request $request): string
|
||||||
{
|
{
|
||||||
$format = $request->getQueryParam('format');
|
$format = $request->getQueryParam('format');
|
||||||
if (!isset($format)) {
|
if (!isset($format)) {
|
||||||
|
@ -126,11 +129,12 @@ abstract class BaseController
|
||||||
*
|
*
|
||||||
* @param Request $request PSR-7 request
|
* @param Request $request PSR-7 request
|
||||||
*
|
*
|
||||||
* @return string Password
|
* @return string|null Password
|
||||||
|
* @throws InvalidURLException
|
||||||
*/
|
*/
|
||||||
protected function getPassword(Request $request)
|
protected function getPassword(Request $request): ?string
|
||||||
{
|
{
|
||||||
$url = $request->getQueryParam('url');
|
$url = $this->getVideoPageUrl($request);
|
||||||
|
|
||||||
$password = $request->getParam('password');
|
$password = $request->getParam('password');
|
||||||
if (isset($password)) {
|
if (isset($password)) {
|
||||||
|
@ -151,10 +155,23 @@ abstract class BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
protected function displayError(Request $request, Response $response, string $message)
|
protected function displayError(Request $request, Response $response, string $message): Response
|
||||||
{
|
{
|
||||||
$controller = new FrontController($this->container);
|
$controller = new FrontController($this->container);
|
||||||
|
|
||||||
return $controller->displayError($request, $response, $message);
|
return $controller->displayError($request, $response, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return string
|
||||||
|
* @throws InvalidURLException
|
||||||
|
*/
|
||||||
|
protected function getVideoPageUrl(Request $request): string
|
||||||
|
{
|
||||||
|
// Prevent SSRF attacks.
|
||||||
|
$parts = Url::validateUrl($request->getQueryParam('url'), new Options());
|
||||||
|
|
||||||
|
return $parts['url'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use Alltube\Library\Exception\YoutubedlException;
|
||||||
use Alltube\Stream\ConvertedPlaylistArchiveStream;
|
use Alltube\Stream\ConvertedPlaylistArchiveStream;
|
||||||
use Alltube\Stream\PlaylistArchiveStream;
|
use Alltube\Stream\PlaylistArchiveStream;
|
||||||
use Alltube\Stream\YoutubeStream;
|
use Alltube\Stream\YoutubeStream;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
use Slim\Http\StatusCode;
|
use Slim\Http\StatusCode;
|
||||||
|
@ -37,56 +38,53 @@ class DownloadController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
|
* @throws InvalidURLException
|
||||||
*/
|
*/
|
||||||
public function download(Request $request, Response $response)
|
public function download(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$url = $request->getQueryParam('url');
|
$url = $this->getVideoPageUrl($request);
|
||||||
|
|
||||||
if (isset($url)) {
|
$this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
|
||||||
$this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||||
// Audio convert.
|
// Audio convert.
|
||||||
return $this->getAudioResponse($request, $response);
|
return $this->getAudioResponse($request, $response);
|
||||||
} elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
|
} elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
|
||||||
// Advance convert.
|
// Advance convert.
|
||||||
return $this->getConvertedResponse($request, $response);
|
return $this->getConvertedResponse($request, $response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular download.
|
// Regular download.
|
||||||
return $this->getDownloadResponse($request, $response);
|
return $this->getDownloadResponse($request, $response);
|
||||||
} catch (PasswordException $e) {
|
} catch (PasswordException $e) {
|
||||||
$frontController = new FrontController($this->container);
|
$frontController = new FrontController($this->container);
|
||||||
|
|
||||||
return $frontController->password($request, $response);
|
return $frontController->password($request, $response);
|
||||||
} catch (WrongPasswordException $e) {
|
} catch (WrongPasswordException $e) {
|
||||||
return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
|
return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
|
||||||
} catch (PlaylistConversionException $e) {
|
} catch (PlaylistConversionException $e) {
|
||||||
|
return $this->displayError(
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
$this->localeManager->t('Conversion of playlists is not supported.')
|
||||||
|
);
|
||||||
|
} catch (InvalidProtocolConversionException $e) {
|
||||||
|
if (in_array($this->video->protocol, ['m3u8', 'm3u8_native'])) {
|
||||||
return $this->displayError(
|
return $this->displayError(
|
||||||
$request,
|
$request,
|
||||||
$response,
|
$response,
|
||||||
$this->localeManager->t('Conversion of playlists is not supported.')
|
$this->localeManager->t('Conversion of M3U8 files is not supported.')
|
||||||
);
|
);
|
||||||
} catch (InvalidProtocolConversionException $e) {
|
} elseif ($this->video->protocol == 'http_dash_segments') {
|
||||||
if (in_array($this->video->protocol, ['m3u8', 'm3u8_native'])) {
|
return $this->displayError(
|
||||||
return $this->displayError(
|
$request,
|
||||||
$request,
|
$response,
|
||||||
$response,
|
$this->localeManager->t('Conversion of DASH segments is not supported.')
|
||||||
$this->localeManager->t('Conversion of M3U8 files is not supported.')
|
);
|
||||||
);
|
} else {
|
||||||
} elseif ($this->video->protocol == 'http_dash_segments') {
|
throw $e;
|
||||||
return $this->displayError(
|
|
||||||
$request,
|
|
||||||
$response,
|
|
||||||
$this->localeManager->t('Conversion of DASH segments is not supported.')
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return $response->withRedirect($this->router->pathFor('index'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +97,7 @@ class DownloadController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
private function getConvertedAudioResponse(Request $request, Response $response)
|
private function getConvertedAudioResponse(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$from = null;
|
$from = null;
|
||||||
$to = null;
|
$to = null;
|
||||||
|
@ -135,7 +133,7 @@ class DownloadController extends BaseController
|
||||||
* @throws PasswordException
|
* @throws PasswordException
|
||||||
* @throws WrongPasswordException
|
* @throws WrongPasswordException
|
||||||
*/
|
*/
|
||||||
private function getAudioResponse(Request $request, Response $response)
|
private function getAudioResponse(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) {
|
if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) {
|
||||||
// Force convert when we need to seek.
|
// Force convert when we need to seek.
|
||||||
|
@ -174,7 +172,7 @@ class DownloadController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
private function getStream(Request $request, Response $response)
|
private function getStream(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
if (isset($this->video->entries)) {
|
if (isset($this->video->entries)) {
|
||||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||||
|
@ -222,13 +220,12 @@ class DownloadController extends BaseController
|
||||||
if ($request->isGet()) {
|
if ($request->isGet()) {
|
||||||
$response = $response->withBody($body);
|
$response = $response->withBody($body);
|
||||||
}
|
}
|
||||||
$response = $response->withHeader(
|
|
||||||
|
return $response->withHeader(
|
||||||
'Content-Disposition',
|
'Content-Disposition',
|
||||||
'attachment; filename="' .
|
'attachment; filename="' .
|
||||||
$this->video->getFilename() . '"'
|
$this->video->getFilename() . '"'
|
||||||
);
|
);
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,7 +237,7 @@ class DownloadController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
private function getRemuxStream(Request $request, Response $response)
|
private function getRemuxStream(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
if (!$this->config->remux) {
|
if (!$this->config->remux) {
|
||||||
throw new RemuxException('You need to enable remux mode to merge two formats.');
|
throw new RemuxException('You need to enable remux mode to merge two formats.');
|
||||||
|
@ -267,7 +264,7 @@ class DownloadController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
private function getDownloadResponse(Request $request, Response $response)
|
private function getDownloadResponse(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$videoUrls = $this->video->getUrl();
|
$videoUrls = $this->video->getUrl();
|
||||||
|
@ -306,7 +303,7 @@ class DownloadController extends BaseController
|
||||||
* @throws YoutubedlException
|
* @throws YoutubedlException
|
||||||
* @throws PopenStreamException
|
* @throws PopenStreamException
|
||||||
*/
|
*/
|
||||||
private function getConvertedResponse(Request $request, Response $response)
|
private function getConvertedResponse(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$response = $response->withHeader(
|
$response = $response->withHeader(
|
||||||
'Content-Disposition',
|
'Content-Disposition',
|
||||||
|
|
|
@ -12,7 +12,10 @@ use Alltube\Library\Exception\WrongPasswordException;
|
||||||
use Alltube\Locale;
|
use Alltube\Locale;
|
||||||
use Alltube\Middleware\CspMiddleware;
|
use Alltube\Middleware\CspMiddleware;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||||
use Slim\Http\StatusCode;
|
use Slim\Http\StatusCode;
|
||||||
|
use Slim\Http\Uri;
|
||||||
|
use stdClass;
|
||||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
@ -52,7 +55,7 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
public function index(Request $request, Response $response)
|
public function index(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$this->view->render(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
|
@ -60,7 +63,7 @@ class FrontController extends BaseController
|
||||||
[
|
[
|
||||||
'class' => 'index',
|
'class' => 'index',
|
||||||
'description' => $this->localeManager->t(
|
'description' => $this->localeManager->t(
|
||||||
'Easily download videos from Youtube, Dailymotion, Vimeo and other websites.'
|
'Easily download videos from YouTube, Dailymotion, Vimeo and other websites.'
|
||||||
),
|
),
|
||||||
'supportedLocales' => $this->localeManager->getSupportedLocales(),
|
'supportedLocales' => $this->localeManager->getSupportedLocales(),
|
||||||
]
|
]
|
||||||
|
@ -78,7 +81,7 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function locale(Request $request, Response $response, array $data)
|
public function locale(Request $request, Response $response, array $data): Response
|
||||||
{
|
{
|
||||||
$this->localeManager->setLocale(new Locale($data['locale']));
|
$this->localeManager->setLocale(new Locale($data['locale']));
|
||||||
|
|
||||||
|
@ -94,7 +97,7 @@ class FrontController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
public function extractors(Request $request, Response $response)
|
public function extractors(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$this->view->render(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
|
@ -103,7 +106,7 @@ class FrontController extends BaseController
|
||||||
'extractors' => $this->downloader->getExtractors(),
|
'extractors' => $this->downloader->getExtractors(),
|
||||||
'class' => 'extractors',
|
'class' => 'extractors',
|
||||||
'title' => $this->localeManager->t('Supported websites'),
|
'title' => $this->localeManager->t('Supported websites'),
|
||||||
'description' => $this->localeManager->t('List of all supported websites from which Alltube Download ' .
|
'description' => $this->localeManager->t('List of all supported websites from which AllTube Download ' .
|
||||||
'can extract video or audio files'),
|
'can extract video or audio files'),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -119,7 +122,7 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
public function password(Request $request, Response $response)
|
public function password(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$this->view->render(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
|
@ -128,7 +131,7 @@ class FrontController extends BaseController
|
||||||
'class' => 'password',
|
'class' => 'password',
|
||||||
'title' => $this->localeManager->t('Password prompt'),
|
'title' => $this->localeManager->t('Password prompt'),
|
||||||
'description' => $this->localeManager->t(
|
'description' => $this->localeManager->t(
|
||||||
'You need a password in order to download this video with Alltube Download'
|
'You need a password in order to download this video with AllTube Download'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -145,7 +148,7 @@ class FrontController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
private function getInfoResponse(Request $request, Response $response)
|
private function getInfoResponse(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->video->getJson();
|
$this->video->getJson();
|
||||||
|
@ -175,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(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
$template,
|
$template,
|
||||||
|
@ -184,6 +231,7 @@ class FrontController extends BaseController
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
'defaultFormat' => $this->defaultFormat,
|
'defaultFormat' => $this->defaultFormat,
|
||||||
|
'formats' => $formats
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -198,24 +246,21 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
|
* @throws InvalidURLException
|
||||||
*/
|
*/
|
||||||
public function info(Request $request, Response $response)
|
public function info(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
|
$url = $this->getVideoPageUrl($request);
|
||||||
|
|
||||||
if (isset($url) && !empty($url)) {
|
$this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
|
||||||
$this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
|
|
||||||
|
|
||||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||||
// We skip the info page and get directly to the download.
|
// We skip the info page and get directly to the download.
|
||||||
return $response->withRedirect(
|
return $response->withRedirect(
|
||||||
$this->router->pathFor('download', [], $request->getQueryParams())
|
$this->router->pathFor('download', [], $request->getQueryParams())
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return $this->getInfoResponse($request, $response);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return $response->withRedirect($this->router->pathFor('index'));
|
return $this->getInfoResponse($request, $response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +273,7 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
protected function displayError(Request $request, Response $response, string $message)
|
protected function displayError(Request $request, Response $response, string $message): Response
|
||||||
{
|
{
|
||||||
$this->view->render(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
|
@ -248,7 +293,7 @@ class FrontController extends BaseController
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function notFound(Request $request, Response $response)
|
public function notFound(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
return $this->displayError($request, $response, $this->localeManager->t('Page not found'))
|
return $this->displayError($request, $response, $this->localeManager->t('Page not found'))
|
||||||
->withStatus(StatusCode::HTTP_NOT_FOUND);
|
->withStatus(StatusCode::HTTP_NOT_FOUND);
|
||||||
|
@ -259,7 +304,7 @@ class FrontController extends BaseController
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function notAllowed(Request $request, Response $response)
|
public function notAllowed(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
return $this->displayError($request, $response, $this->localeManager->t('Method not allowed'))
|
return $this->displayError($request, $response, $this->localeManager->t('Method not allowed'))
|
||||||
->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED);
|
->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED);
|
||||||
|
@ -274,7 +319,7 @@ class FrontController extends BaseController
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
public function error(Request $request, Response $response, Throwable $error)
|
public function error(Request $request, Response $response, Throwable $error): Response
|
||||||
{
|
{
|
||||||
$this->logger->error($error);
|
$this->logger->error($error);
|
||||||
|
|
||||||
|
@ -285,7 +330,7 @@ class FrontController extends BaseController
|
||||||
$response = $cspMiddleware->applyHeader($response);
|
$response = $cspMiddleware->applyHeader($response);
|
||||||
|
|
||||||
if ($this->config->debug) {
|
if ($this->config->debug) {
|
||||||
$renderer = new HtmlErrorRenderer(true);
|
$renderer = new HtmlErrorRenderer(true, null, null, $this->container->get('root_path'));
|
||||||
$exception = $renderer->render($error);
|
$exception = $renderer->render($error);
|
||||||
|
|
||||||
$response->getBody()->write($exception->getAsString());
|
$response->getBody()->write($exception->getAsString());
|
||||||
|
@ -304,4 +349,24 @@ class FrontController extends BaseController
|
||||||
return $this->displayError($request, $response, $message);
|
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,6 +7,7 @@
|
||||||
namespace Alltube\Controller;
|
namespace Alltube\Controller;
|
||||||
|
|
||||||
use Alltube\Library\Exception\AlltubeLibraryException;
|
use Alltube\Library\Exception\AlltubeLibraryException;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
use Slim\Http\StatusCode;
|
use Slim\Http\StatusCode;
|
||||||
|
@ -25,11 +26,11 @@ class JsonController extends BaseController
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
public function json(Request $request, Response $response)
|
public function json(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$url = $request->getQueryParam('url');
|
try {
|
||||||
|
$url = $this->getVideoPageUrl($request);
|
||||||
|
|
||||||
if (isset($url)) {
|
|
||||||
$this->video = $this->downloader->getVideo(
|
$this->video = $this->downloader->getVideo(
|
||||||
$url,
|
$url,
|
||||||
$this->getFormat($request),
|
$this->getFormat($request),
|
||||||
|
@ -37,8 +38,8 @@ class JsonController extends BaseController
|
||||||
);
|
);
|
||||||
|
|
||||||
return $response->withJson($this->video->getJson());
|
return $response->withJson($this->video->getJson());
|
||||||
} else {
|
} catch (InvalidURLException $e) {
|
||||||
return $response->withJson(['error' => 'You need to provide the url parameter'])
|
return $response->withJson(['error' => $e->getMessage()])
|
||||||
->withStatus(StatusCode::HTTP_BAD_REQUEST);
|
->withStatus(StatusCode::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Alltube;
|
namespace Alltube;
|
||||||
|
|
||||||
|
use Slim\Http\StatusCode;
|
||||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
|
@ -11,25 +12,24 @@ use Throwable;
|
||||||
*/
|
*/
|
||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last resort if the error has not been caught by the Slim error handler for some reason.
|
* Last resort if the error has not been caught by the Slim error handler for some reason.
|
||||||
* @param Throwable $e
|
* @param Throwable $e
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function handle(Throwable $e)
|
public static function handle(Throwable $e): void
|
||||||
{
|
{
|
||||||
error_log($e);
|
error_log($e);
|
||||||
|
|
||||||
if (class_exists(HtmlErrorRenderer::class)) {
|
if (class_exists(HtmlErrorRenderer::class)) {
|
||||||
// If dev dependencies are loaded, we can use symfony/error-handler.
|
// If dev dependencies are loaded, we can use symfony/error-handler.
|
||||||
$renderer = new HtmlErrorRenderer(true);
|
$renderer = new HtmlErrorRenderer(true, null, null, dirname(__DIR__));
|
||||||
$exception = $renderer->render($e);
|
$exception = $renderer->render($e);
|
||||||
|
|
||||||
http_response_code($exception->getStatusCode());
|
http_response_code($exception->getStatusCode());
|
||||||
die($exception->getAsString());
|
die($exception->getAsString());
|
||||||
} else {
|
} else {
|
||||||
http_response_code(500);
|
http_response_code(StatusCode::HTTP_INTERNAL_SERVER_ERROR);
|
||||||
die('Error when starting the app: ' . htmlentities($e->getMessage()));
|
die('Error when starting the app: ' . htmlentities($e->getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,4 @@ use Exception;
|
||||||
*/
|
*/
|
||||||
class ConfigException extends Exception
|
class ConfigException extends Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,4 @@ use Exception;
|
||||||
*/
|
*/
|
||||||
class DependencyException extends Exception
|
class DependencyException extends Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,15 +14,14 @@ use Symfony\Component\ErrorHandler\Debug;
|
||||||
*/
|
*/
|
||||||
class ConfigFactory
|
class ConfigFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Container $container
|
* @param Container $container
|
||||||
* @return Config
|
* @return Config
|
||||||
* @throws ConfigException
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public static function create(Container $container)
|
public static function create(Container $container): Config
|
||||||
{
|
{
|
||||||
$configPath = __DIR__ . '/../../config/config.yml';
|
$configPath = $container->get('root_path') . '/config/config.yml';
|
||||||
if (is_file($configPath)) {
|
if (is_file($configPath)) {
|
||||||
$config = Config::fromFile($configPath);
|
$config = Config::fromFile($configPath);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alltube\Factory;
|
||||||
|
|
||||||
|
use DebugBar\DataCollector\ConfigCollector;
|
||||||
|
use DebugBar\DataCollector\MemoryCollector;
|
||||||
|
use DebugBar\DataCollector\MessagesCollector;
|
||||||
|
use DebugBar\DataCollector\PhpInfoCollector;
|
||||||
|
use DebugBar\DataCollector\RequestDataCollector;
|
||||||
|
use DebugBar\DebugBar;
|
||||||
|
use DebugBar\DebugBarException;
|
||||||
|
use Kitchenu\Debugbar\DataCollector\SlimRouteCollector;
|
||||||
|
use Slim\Container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DebugBarFactory
|
||||||
|
* @package Alltube\Factory
|
||||||
|
*/
|
||||||
|
class DebugBarFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Container $container
|
||||||
|
* @return DebugBar
|
||||||
|
* @throws DebugBarException
|
||||||
|
*/
|
||||||
|
public static function create(Container $container): DebugBar
|
||||||
|
{
|
||||||
|
$debugBar = new DebugBar();
|
||||||
|
|
||||||
|
$requestCollector = new RequestDataCollector();
|
||||||
|
$configCollector = new ConfigCollector(get_object_vars($container->get('config')));
|
||||||
|
|
||||||
|
$debugBar->addCollector(new PhpInfoCollector())
|
||||||
|
->addCollector(new MessagesCollector())
|
||||||
|
->addCollector($requestCollector)
|
||||||
|
->addCollector(new MemoryCollector())
|
||||||
|
->addCollector($configCollector)
|
||||||
|
->addCollector(new SlimRouteCollector($container->get('router'), $container->get('request')));
|
||||||
|
|
||||||
|
$container->get('logger')->add('debugbar', $debugBar->getCollector('messages'));
|
||||||
|
|
||||||
|
$requestCollector->useHtmlVarDumper();
|
||||||
|
$configCollector->useHtmlVarDumper();
|
||||||
|
|
||||||
|
return $debugBar;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ namespace Alltube\Factory;
|
||||||
|
|
||||||
use Alltube\Exception\DependencyException;
|
use Alltube\Exception\DependencyException;
|
||||||
use Alltube\LocaleManager;
|
use Alltube\LocaleManager;
|
||||||
|
use Locale;
|
||||||
use Slim\Container;
|
use Slim\Container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,15 +13,14 @@ use Slim\Container;
|
||||||
*/
|
*/
|
||||||
class LocaleManagerFactory
|
class LocaleManagerFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Container $container
|
* @param Container $container
|
||||||
* @return LocaleManager|null
|
* @return LocaleManager
|
||||||
* @throws DependencyException
|
* @throws DependencyException
|
||||||
*/
|
*/
|
||||||
public static function create(Container $container)
|
public static function create(Container $container): LocaleManager
|
||||||
{
|
{
|
||||||
if (!class_exists('Locale')) {
|
if (!class_exists(Locale::class)) {
|
||||||
throw new DependencyException('You need to install the intl extension for PHP.');
|
throw new DependencyException('You need to install the intl extension for PHP.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
namespace Alltube\Factory;
|
namespace Alltube\Factory;
|
||||||
|
|
||||||
use Consolidation\Log\Logger;
|
use Consolidation\Log\Logger;
|
||||||
|
use Consolidation\Log\LoggerManager;
|
||||||
use Consolidation\Log\LogOutputStyler;
|
use Consolidation\Log\LogOutputStyler;
|
||||||
use Slim\Container;
|
use Slim\Container;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class LoggerFactory
|
* Class LoggerFactory
|
||||||
|
@ -13,23 +15,26 @@ use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
*/
|
*/
|
||||||
class LoggerFactory
|
class LoggerFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Container $container
|
* @param Container $container
|
||||||
* @return Logger
|
* @return LoggerManager
|
||||||
*/
|
*/
|
||||||
public static function create(Container $container)
|
public static function create(Container $container): LoggerManager
|
||||||
{
|
{
|
||||||
$config = $container->get('config');
|
$config = $container->get('config');
|
||||||
if ($config->debug) {
|
if ($config->debug) {
|
||||||
$verbosity = ConsoleOutput::VERBOSITY_DEBUG;
|
$verbosity = OutputInterface::VERBOSITY_DEBUG;
|
||||||
} else {
|
} else {
|
||||||
$verbosity = ConsoleOutput::VERBOSITY_NORMAL;
|
$verbosity = OutputInterface::VERBOSITY_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$loggerManager = new LoggerManager();
|
||||||
|
|
||||||
$logger = new Logger(new ConsoleOutput($verbosity));
|
$logger = new Logger(new ConsoleOutput($verbosity));
|
||||||
$logger->setLogOutputStyler(new LogOutputStyler());
|
$logger->setLogOutputStyler(new LogOutputStyler());
|
||||||
|
|
||||||
return $logger;
|
$loggerManager->add('default', $logger);
|
||||||
|
|
||||||
|
return $loggerManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
namespace Alltube\Factory;
|
namespace Alltube\Factory;
|
||||||
|
|
||||||
use Aura\Session\Session;
|
use Aura\Session\Session;
|
||||||
|
use Aura\Session\SessionFactory as AuraSessionFactory;
|
||||||
use Slim\Container;
|
use Slim\Container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,16 +15,15 @@ use Slim\Container;
|
||||||
*/
|
*/
|
||||||
class SessionFactory
|
class SessionFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current session.
|
* Get the current session.
|
||||||
*
|
*
|
||||||
* @param Container $container
|
* @param Container $container
|
||||||
* @return Session
|
* @return Session
|
||||||
*/
|
*/
|
||||||
public static function create(Container $container)
|
public static function create(Container $container): Session
|
||||||
{
|
{
|
||||||
$session_factory = new \Aura\Session\SessionFactory();
|
$session_factory = new AuraSessionFactory();
|
||||||
$session = $session_factory->newInstance($_COOKIE);
|
$session = $session_factory->newInstance($_COOKIE);
|
||||||
|
|
||||||
$session->setCookieParams(['httponly' => true]);
|
$session->setCookieParams(['httponly' => true]);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
namespace Alltube\Factory;
|
namespace Alltube\Factory;
|
||||||
|
|
||||||
use Alltube\LocaleManager;
|
use Alltube\LocaleManager;
|
||||||
|
use Junker\DebugBar\Bridge\SmartyCollector;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Uri;
|
use Slim\Http\Uri;
|
||||||
|
@ -20,39 +21,41 @@ use SmartyException;
|
||||||
class ViewFactory
|
class ViewFactory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Generate the canonical URL of the current page.
|
* @param Uri $uri
|
||||||
*
|
* @return Uri
|
||||||
* @param Request $request PSR-7 Request
|
|
||||||
*
|
|
||||||
* @return string URL
|
|
||||||
*/
|
*/
|
||||||
private static function getCanonicalUrl(Request $request)
|
private static function cleanBasePath(Uri $uri): Uri
|
||||||
{
|
{
|
||||||
/** @var Uri $uri */
|
$basePath = $uri->getBasePath();
|
||||||
$uri = $request->getUri();
|
if (str_ends_with($basePath, 'index.php')) {
|
||||||
|
$basePath = dirname($basePath);
|
||||||
|
if ($basePath == '/') {
|
||||||
|
/*
|
||||||
|
* Calling withBasePath('/') does nothing,
|
||||||
|
* we have to use an empty string instead.
|
||||||
|
*/
|
||||||
|
$basePath = '';
|
||||||
|
}
|
||||||
|
|
||||||
return $uri->withBasePath('')
|
/*
|
||||||
->withHost('alltubedownload.net')
|
* When the base path ends with index.php,
|
||||||
->withScheme('https');
|
* routing works correctly, but it breaks the URL of static assets using {base_url}.
|
||||||
|
* So we alter the base path but only in the URI used by SmartyPlugins.
|
||||||
|
*/
|
||||||
|
$uri = $uri->withBasePath($basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Smarty view object.
|
* Create a URI suitable for templates.
|
||||||
*
|
*
|
||||||
* @param ContainerInterface $container Slim dependency container
|
* @param Request $request
|
||||||
* @param Request|null $request PSR-7 request
|
* @return Uri
|
||||||
*
|
|
||||||
* @return Smarty
|
|
||||||
* @throws SmartyException
|
|
||||||
*/
|
*/
|
||||||
public static function create(ContainerInterface $container, Request $request = null)
|
public static function prepareUri(Request $request): Uri
|
||||||
{
|
{
|
||||||
if (!isset($request)) {
|
|
||||||
$request = $container->get('request');
|
|
||||||
}
|
|
||||||
|
|
||||||
$view = new Smarty(__DIR__ . '/../../templates/');
|
|
||||||
|
|
||||||
/** @var Uri $uri */
|
/** @var Uri $uri */
|
||||||
$uri = $request->getUri();
|
$uri = $request->getUri();
|
||||||
if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) {
|
if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) {
|
||||||
|
@ -72,6 +75,28 @@ class ViewFactory
|
||||||
$uri = $uri->withBasePath($path);
|
$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 */
|
/** @var LocaleManager $localeManager */
|
||||||
$localeManager = $container->get('locale');
|
$localeManager = $container->get('locale');
|
||||||
|
|
||||||
|
@ -80,11 +105,24 @@ class ViewFactory
|
||||||
$view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);
|
$view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);
|
||||||
$view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);
|
$view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);
|
||||||
|
|
||||||
$view->offsetSet('canonical', self::getCanonicalUrl($request));
|
$view->offsetSet('locale', $container->get('locale'));
|
||||||
$view->offsetSet('locale', $container->get('locale')->getLocale());
|
|
||||||
$view->offsetSet('config', $container->get('config'));
|
$view->offsetSet('config', $container->get('config'));
|
||||||
$view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl());
|
$view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl());
|
||||||
|
|
||||||
|
if ($container->has('debugbar')) {
|
||||||
|
$debugBar = $container->get('debugbar');
|
||||||
|
$collector = new SmartyCollector($view->getSmarty());
|
||||||
|
$collector->useHtmlVarDumper();
|
||||||
|
$debugBar->addCollector($collector);
|
||||||
|
|
||||||
|
$view->offsetSet(
|
||||||
|
'debug_render',
|
||||||
|
$debugBar->getJavascriptRenderer(
|
||||||
|
$uri->getBaseUrl() . '/vendor/maximebf/debugbar/src/DebugBar/Resources/'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ class Locale
|
||||||
*
|
*
|
||||||
* @return string ISO 15897 code
|
* @return string ISO 15897 code
|
||||||
*/
|
*/
|
||||||
public function __toString()
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
return $this->getIso15897();
|
return $this->getIso15897();
|
||||||
}
|
}
|
||||||
|
@ -58,9 +58,9 @@ class Locale
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFullName()
|
public function getFullName(): string
|
||||||
{
|
{
|
||||||
return PHPLocale::getDisplayName($this->getIso15897(), $this->getIso15897());
|
return mb_convert_case(PHPLocale::getDisplayName($this->getIso15897(), $this->getIso15897()), MB_CASE_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +68,7 @@ class Locale
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getIso15897()
|
public function getIso15897(): string
|
||||||
{
|
{
|
||||||
if (isset($this->region)) {
|
if (isset($this->region)) {
|
||||||
return $this->language . '_' . $this->region;
|
return $this->language . '_' . $this->region;
|
||||||
|
@ -82,7 +82,7 @@ class Locale
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getBcp47()
|
public function getBcp47(): string
|
||||||
{
|
{
|
||||||
if (isset($this->region)) {
|
if (isset($this->region)) {
|
||||||
return $this->language . '-' . $this->region;
|
return $this->language . '-' . $this->region;
|
||||||
|
@ -96,7 +96,7 @@ class Locale
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getIso3166()
|
public function getIso3166(): string
|
||||||
{
|
{
|
||||||
return strtolower($this->region);
|
return strtolower($this->region);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ use Symfony\Component\Translation\Loader\PoFileLoader;
|
||||||
*/
|
*/
|
||||||
class LocaleManager
|
class LocaleManager
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path to locales.
|
* Path to locales.
|
||||||
*/
|
*/
|
||||||
|
@ -28,14 +27,14 @@ class LocaleManager
|
||||||
*
|
*
|
||||||
* @var Locale|null
|
* @var Locale|null
|
||||||
*/
|
*/
|
||||||
private $curLocale;
|
private ?Locale $curLocale = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session segment used to store session variables.
|
* Session segment used to store session variables.
|
||||||
*
|
*
|
||||||
* @var Segment
|
* @var Segment
|
||||||
*/
|
*/
|
||||||
private $sessionSegment;
|
private Segment $sessionSegment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default locale.
|
* Default locale.
|
||||||
|
@ -49,7 +48,7 @@ class LocaleManager
|
||||||
*
|
*
|
||||||
* @var Translator
|
* @var Translator
|
||||||
*/
|
*/
|
||||||
private $translator;
|
private Translator $translator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LocaleManager constructor.
|
* LocaleManager constructor.
|
||||||
|
@ -80,7 +79,7 @@ class LocaleManager
|
||||||
*
|
*
|
||||||
* @return Locale[]
|
* @return Locale[]
|
||||||
*/
|
*/
|
||||||
public function getSupportedLocales()
|
public function getSupportedLocales(): array
|
||||||
{
|
{
|
||||||
$return = [
|
$return = [
|
||||||
new Locale('en_US')
|
new Locale('en_US')
|
||||||
|
@ -103,7 +102,7 @@ class LocaleManager
|
||||||
*
|
*
|
||||||
* @return Locale|null
|
* @return Locale|null
|
||||||
*/
|
*/
|
||||||
public function getLocale()
|
public function getLocale(): ?Locale
|
||||||
{
|
{
|
||||||
return $this->curLocale;
|
return $this->curLocale;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +113,7 @@ class LocaleManager
|
||||||
* @param Locale $locale Locale
|
* @param Locale $locale Locale
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function setLocale(Locale $locale)
|
public function setLocale(Locale $locale): void
|
||||||
{
|
{
|
||||||
$this->translator->setLocale($locale->getIso15897());
|
$this->translator->setLocale($locale->getIso15897());
|
||||||
$this->curLocale = $locale;
|
$this->curLocale = $locale;
|
||||||
|
@ -125,7 +124,7 @@ class LocaleManager
|
||||||
* Unset the current locale.
|
* Unset the current locale.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function unsetLocale()
|
public function unsetLocale(): void
|
||||||
{
|
{
|
||||||
$this->translator->setLocale(self::DEFAULT_LOCALE);
|
$this->translator->setLocale(self::DEFAULT_LOCALE);
|
||||||
$this->curLocale = null;
|
$this->curLocale = null;
|
||||||
|
@ -135,14 +134,14 @@ class LocaleManager
|
||||||
/**
|
/**
|
||||||
* Smarty "t" block.
|
* Smarty "t" block.
|
||||||
*
|
*
|
||||||
* @param mixed[] $params Block parameters
|
* @param string[]|string[][] $params Block parameters
|
||||||
* @param string|null $text Block content
|
* @param string|null $text Block content
|
||||||
*
|
*
|
||||||
* @return string Translated string
|
* @return string Translated string
|
||||||
*/
|
*/
|
||||||
public function smartyTranslate(array $params, string $text = null)
|
public function smartyTranslate(array $params, string $text = null): string
|
||||||
{
|
{
|
||||||
if (isset($params['params'])) {
|
if (isset($params['params']) && is_array($params['params'])) {
|
||||||
return $this->t($text, $params['params']);
|
return $this->t($text, $params['params']);
|
||||||
} else {
|
} else {
|
||||||
return $this->t($text);
|
return $this->t($text);
|
||||||
|
@ -154,10 +153,10 @@ class LocaleManager
|
||||||
*
|
*
|
||||||
* @param string|null $string $string String to translate
|
* @param string|null $string $string String to translate
|
||||||
*
|
*
|
||||||
* @param mixed[] $params
|
* @param string[] $params
|
||||||
* @return string Translated string
|
* @return string Translated string
|
||||||
*/
|
*/
|
||||||
public function t(string $string = null, array $params = [])
|
public function t(string $string = null, array $params = []): string
|
||||||
{
|
{
|
||||||
if (isset($string)) {
|
if (isset($string)) {
|
||||||
return $this->translator->trans($string, $params);
|
return $this->translator->trans($string, $params);
|
||||||
|
|
|
@ -15,7 +15,6 @@ use Slim\Http\Response;
|
||||||
*/
|
*/
|
||||||
class CspMiddleware
|
class CspMiddleware
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Config
|
* @var Config
|
||||||
*/
|
*/
|
||||||
|
@ -34,10 +33,11 @@ class CspMiddleware
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @return MessageInterface
|
* @return MessageInterface
|
||||||
*/
|
*/
|
||||||
public function applyHeader(Response $response)
|
public function applyHeader(Response $response): MessageInterface
|
||||||
{
|
{
|
||||||
$csp = new CSPBuilder();
|
$csp = new CSPBuilder();
|
||||||
$csp->addDirective('default-src', [])
|
$csp->disableOldBrowserSupport()
|
||||||
|
->addDirective('default-src', [])
|
||||||
->addDirective('font-src', ['self' => true])
|
->addDirective('font-src', ['self' => true])
|
||||||
->addDirective('style-src', ['self' => true])
|
->addDirective('style-src', ['self' => true])
|
||||||
->addDirective('manifest-src', ['self' => true])
|
->addDirective('manifest-src', ['self' => true])
|
||||||
|
@ -47,9 +47,10 @@ class CspMiddleware
|
||||||
->addSource('img-src', '*');
|
->addSource('img-src', '*');
|
||||||
|
|
||||||
if ($this->config->debug) {
|
if ($this->config->debug) {
|
||||||
// So symfony/debug and symfony/error-handler can work.
|
// So maximebf/debugbar, symfony/debug and symfony/error-handler can work.
|
||||||
$csp->setDirective('script-src', ['unsafe-inline' => true])
|
$csp->setDirective('script-src', ['self' => true, 'unsafe-inline' => true])
|
||||||
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true]);
|
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true])
|
||||||
|
->addSource('img-src', 'data:');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $csp->injectCSPHeader($response);
|
return $csp->injectCSPHeader($response);
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
namespace Alltube\Middleware;
|
namespace Alltube\Middleware;
|
||||||
|
|
||||||
use Psr\Container\ContainerInterface;
|
use Alltube\Factory\ViewFactory;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
use Slim\Router;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class LinkHeaderMiddleware
|
* Class LinkHeaderMiddleware
|
||||||
|
@ -13,20 +12,6 @@ use Slim\Router;
|
||||||
*/
|
*/
|
||||||
class LinkHeaderMiddleware
|
class LinkHeaderMiddleware
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Router
|
|
||||||
*/
|
|
||||||
private $router;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LinkHeaderMiddleware constructor.
|
|
||||||
* @param ContainerInterface $container
|
|
||||||
*/
|
|
||||||
public function __construct(ContainerInterface $container)
|
|
||||||
{
|
|
||||||
$this->router = $container->get('router');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
|
@ -35,12 +20,19 @@ class LinkHeaderMiddleware
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, Response $response, callable $next)
|
public function __invoke(Request $request, Response $response, callable $next)
|
||||||
{
|
{
|
||||||
|
$uri = ViewFactory::prepareUri($request);
|
||||||
|
|
||||||
$response = $response->withHeader(
|
$response = $response->withHeader(
|
||||||
'Link',
|
'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);
|
return $next($request, $response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,11 @@ class LocaleMiddleware
|
||||||
/**
|
/**
|
||||||
* Test if a locale can be used for the current user.
|
* 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
|
* @return Locale|null Locale if chosen, nothing otherwise
|
||||||
*/
|
*/
|
||||||
public function testLocale(array $proposedLocale)
|
public function testLocale(array $proposedLocale): ?Locale
|
||||||
{
|
{
|
||||||
foreach ($this->localeManager->getSupportedLocales() as $locale) {
|
foreach ($this->localeManager->getSupportedLocales() as $locale) {
|
||||||
$parsedLocale = AcceptLanguage::parse($locale);
|
$parsedLocale = AcceptLanguage::parse($locale);
|
||||||
|
@ -67,7 +67,7 @@ class LocaleMiddleware
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, Response $response, callable $next)
|
public function __invoke(Request $request, Response $response, callable $next): Response
|
||||||
{
|
{
|
||||||
$headers = $request->getHeader('Accept-Language');
|
$headers = $request->getHeader('Accept-Language');
|
||||||
$curLocale = $this->localeManager->getLocale();
|
$curLocale = $this->localeManager->getLocale();
|
||||||
|
|
|
@ -14,7 +14,6 @@ use Robo\Tasks;
|
||||||
*/
|
*/
|
||||||
class ReleaseCommand extends Tasks
|
class ReleaseCommand extends Tasks
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create release archive
|
* Create release archive
|
||||||
* @return void
|
* @return void
|
||||||
|
@ -27,12 +26,12 @@ class ReleaseCommand extends Tasks
|
||||||
$gitTask = $this->taskExec('git');
|
$gitTask = $this->taskExec('git');
|
||||||
$result = $gitTask
|
$result = $gitTask
|
||||||
->arg('describe')
|
->arg('describe')
|
||||||
|
->interactive(false)
|
||||||
->run();
|
->run();
|
||||||
$result->provideOutputdata();
|
|
||||||
|
|
||||||
$tmpDir = $this->_tmpDir();
|
$tmpDir = $this->_tmpDir();
|
||||||
|
|
||||||
$filename = 'alltube-' . trim((string)$result->getOutputData()) . '.zip';
|
$filename = 'alltube-' . trim($result->getMessage()) . '.zip';
|
||||||
|
|
||||||
/** @var FilesystemStack $rmTask */
|
/** @var FilesystemStack $rmTask */
|
||||||
$rmTask = $this->taskFilesystemStack();
|
$rmTask = $this->taskFilesystemStack();
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ConvertedPlaylistArchiveStream extends PlaylistArchiveStream
|
||||||
* @return void
|
* @return void
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
protected function startVideoStream(Video $video)
|
protected function startVideoStream(Video $video): void
|
||||||
{
|
{
|
||||||
$this->curVideoStream = new Stream($this->downloader->getAudioStream($video));
|
$this->curVideoStream = new Stream($this->downloader->getAudioStream($video));
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @var Video[]
|
* @var Video[]
|
||||||
*/
|
*/
|
||||||
private $videos = [];
|
private array $videos = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream used to store data before it is sent to the browser.
|
* Stream used to store data before it is sent to the browser.
|
||||||
|
@ -38,21 +38,21 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @var StreamInterface
|
* @var StreamInterface
|
||||||
*/
|
*/
|
||||||
protected $curVideoStream;
|
protected StreamInterface $curVideoStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the archive is complete.
|
* True if the archive is complete.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $isComplete = false;
|
private bool $isComplete = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloader object.
|
* Downloader object.
|
||||||
*
|
*
|
||||||
* @var Downloader
|
* @var Downloader
|
||||||
*/
|
*/
|
||||||
protected $downloader;
|
protected Downloader $downloader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PlaylistArchiveStream constructor.
|
* PlaylistArchiveStream constructor.
|
||||||
|
@ -83,7 +83,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function send($data)
|
protected function send($data): void
|
||||||
{
|
{
|
||||||
$pos = $this->tell();
|
$pos = $this->tell();
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return int|null
|
* @return int|null
|
||||||
*/
|
*/
|
||||||
public function getSize()
|
public function getSize(): ?int
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isSeekable()
|
public function isSeekable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function rewind()
|
public function rewind(): void
|
||||||
{
|
{
|
||||||
rewind($this->buffer);
|
rewind($this->buffer);
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isWritable()
|
public function isWritable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isReadable()
|
public function isReadable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @param string|null $key string $key Specific metadata to retrieve.
|
* @param string|null $key string $key Specific metadata to retrieve.
|
||||||
*
|
*
|
||||||
* @return array|mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
public function getMetadata($key = null)
|
public function getMetadata($key = null)
|
||||||
{
|
{
|
||||||
|
@ -208,7 +208,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function __toString()
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
$this->rewind();
|
$this->rewind();
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function seek($offset, $whence = SEEK_SET)
|
public function seek($offset, $whence = SEEK_SET): void
|
||||||
{
|
{
|
||||||
fseek($this->buffer, $offset, $whence);
|
fseek($this->buffer, $offset, $whence);
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function eof()
|
public function eof(): bool
|
||||||
{
|
{
|
||||||
return $this->isComplete && feof($this->buffer);
|
return $this->isComplete && feof($this->buffer);
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
* @return void
|
* @return void
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
protected function startVideoStream(Video $video)
|
protected function startVideoStream(Video $video): void
|
||||||
{
|
{
|
||||||
$response = $this->downloader->getHttpResponse($video);
|
$response = $this->downloader->getHttpResponse($video);
|
||||||
|
|
||||||
|
@ -272,12 +272,12 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
/**
|
/**
|
||||||
* Read data from the stream.
|
* Read data from the stream.
|
||||||
*
|
*
|
||||||
* @param mixed $count Number of bytes to read
|
* @param mixed $length Number of bytes to read
|
||||||
*
|
*
|
||||||
* @return string|false
|
* @return string|false
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException
|
||||||
*/
|
*/
|
||||||
public function read($count)
|
public function read($length)
|
||||||
{
|
{
|
||||||
// If the archive is complete, we only read the remaining buffer.
|
// If the archive is complete, we only read the remaining buffer.
|
||||||
if (!$this->isComplete) {
|
if (!$this->isComplete) {
|
||||||
|
@ -297,15 +297,22 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Continue streaming the current video.
|
// Continue streaming the current video.
|
||||||
$this->stream_file_part($this->curVideoStream->read($count));
|
$this->stream_file_part($this->curVideoStream->read($length));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Start streaming the first video.
|
// Start streaming the first video.
|
||||||
$this->startVideoStream(current($this->videos));
|
$video = current($this->videos);
|
||||||
|
if ($video) {
|
||||||
|
$this->startVideoStream($video);
|
||||||
|
} else {
|
||||||
|
$this->push_error('Playlist was empty');
|
||||||
|
$this->finish();
|
||||||
|
$this->isComplete = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fread($this->buffer, $count);
|
return fread($this->buffer, $length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,7 +320,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function close()
|
public function close(): void
|
||||||
{
|
{
|
||||||
if (is_resource($this->buffer)) {
|
if (is_resource($this->buffer)) {
|
||||||
fclose($this->buffer);
|
fclose($this->buffer);
|
||||||
|
|
|
@ -20,7 +20,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @var ResponseInterface
|
* @var ResponseInterface
|
||||||
*/
|
*/
|
||||||
private $response;
|
private ResponseInterface $response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* YoutubeChunkStream constructor.
|
* YoutubeChunkStream constructor.
|
||||||
|
@ -39,7 +39,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function read($length)
|
public function read($length): string
|
||||||
{
|
{
|
||||||
$size = intval($this->response->getHeader('Content-Length')[0]);
|
$size = intval($this->response->getHeader('Content-Length')[0]);
|
||||||
if ($size - $this->tell() < $length) {
|
if ($size - $this->tell() < $length) {
|
||||||
|
@ -53,7 +53,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
/**
|
/**
|
||||||
* Reads all data from the stream into a string, from the beginning to end.
|
* Reads all data from the stream into a string, from the beginning to end.
|
||||||
*/
|
*/
|
||||||
public function __toString()
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
return (string)$this->response->getBody();
|
return (string)$this->response->getBody();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function close()
|
public function close(): void
|
||||||
{
|
{
|
||||||
$this->response->getBody()->close();
|
$this->response->getBody()->close();
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return int|null
|
* @return int|null
|
||||||
*/
|
*/
|
||||||
public function getSize()
|
public function getSize(): ?int
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->getSize();
|
return $this->response->getBody()->getSize();
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function tell()
|
public function tell(): int
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->tell();
|
return $this->response->getBody()->tell();
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function eof()
|
public function eof(): bool
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->eof();
|
return $this->response->getBody()->eof();
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isSeekable()
|
public function isSeekable(): bool
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->isSeekable();
|
return $this->response->getBody()->isSeekable();
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function seek($offset, $whence = SEEK_SET)
|
public function seek($offset, $whence = SEEK_SET): void
|
||||||
{
|
{
|
||||||
$this->response->getBody()->seek($offset, $whence);
|
$this->response->getBody()->seek($offset, $whence);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function rewind()
|
public function rewind(): void
|
||||||
{
|
{
|
||||||
$this->response->getBody()->rewind();
|
$this->response->getBody()->rewind();
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isWritable()
|
public function isWritable(): bool
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->isWritable();
|
return $this->response->getBody()->isWritable();
|
||||||
}
|
}
|
||||||
|
@ -156,9 +156,9 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @param mixed $string The string that is to be written
|
* @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);
|
return $this->response->getBody()->write($string);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isReadable()
|
public function isReadable(): bool
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->isReadable();
|
return $this->response->getBody()->isReadable();
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getContents()
|
public function getContents(): string
|
||||||
{
|
{
|
||||||
return $this->response->getBody()->getContents();
|
return $this->response->getBody()->getContents();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ class YoutubeChunkStream implements StreamInterface
|
||||||
*
|
*
|
||||||
* @param string|null $key Specific metadata to retrieve.
|
* @param string|null $key Specific metadata to retrieve.
|
||||||
*
|
*
|
||||||
* @return array|mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
public function getMetadata($key = null)
|
public function getMetadata($key = null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,11 +22,11 @@ class UglyRouter extends Router
|
||||||
*
|
*
|
||||||
* @param ServerRequestInterface $request The current HTTP request object
|
* @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
|
* @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
|
||||||
*/
|
*/
|
||||||
public function dispatch(ServerRequestInterface $request)
|
public function dispatch(ServerRequestInterface $request): array
|
||||||
{
|
{
|
||||||
$params = $request->getQueryParams();
|
$params = $request->getQueryParams();
|
||||||
$uri = new Uri('', '');
|
$uri = new Uri('', '');
|
||||||
|
@ -53,9 +53,9 @@ class UglyRouter extends Router
|
||||||
* @throws InvalidArgumentException If required data not provided
|
* @throws InvalidArgumentException If required data not provided
|
||||||
* @throws RuntimeException If named route does not exist
|
* @throws RuntimeException If named route does not exist
|
||||||
*/
|
*/
|
||||||
public function pathFor($name, array $data = [], array $queryParams = [])
|
public function pathFor($name, array $data = [], array $queryParams = []): string
|
||||||
{
|
{
|
||||||
$queryParams['page'] = $name;
|
$queryParams['page'] = $this->relativePathFor($name, $data);
|
||||||
$url = Uri::createFromString($this->relativePathFor($name, $data, $queryParams))->withPath('');
|
$url = Uri::createFromString($this->relativePathFor($name, $data, $queryParams))->withPath('');
|
||||||
|
|
||||||
if ($this->basePath) {
|
if ($this->basePath) {
|
||||||
|
|
101
composer.json
101
composer.json
|
@ -1,9 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "rudloff/alltube",
|
"name": "rudloff/alltube",
|
||||||
"type": "project",
|
|
||||||
"description": "HTML GUI for youtube-dl",
|
"description": "HTML GUI for youtube-dl",
|
||||||
"homepage": "http://alltubedownload.net/",
|
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
|
"type": "project",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Pierre Rudloff",
|
"name": "Pierre Rudloff",
|
||||||
|
@ -14,48 +13,86 @@
|
||||||
{
|
{
|
||||||
"name": "Olivier Haquette",
|
"name": "Olivier Haquette",
|
||||||
"email": "contact@olivierhaquette.fr",
|
"email": "contact@olivierhaquette.fr",
|
||||||
"homepage": "http://olivierhaquette.fr/",
|
"homepage": "https://ographik.fr/",
|
||||||
"role": "Designer"
|
"role": "Designer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.3",
|
"php": ">=7.4",
|
||||||
"ext-intl": "*",
|
"ext-intl": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"aura/session": "^2.1",
|
"aura/session": "^2.1",
|
||||||
"barracudanetworks/archivestream-php": "^1.0",
|
"barracudanetworks/archivestream-php": "^1.0",
|
||||||
"consolidation/log": "^2.0",
|
"consolidation/log": "^2.0",
|
||||||
|
"cweagans/composer-patches": "^1.7",
|
||||||
|
"j0k3r/httplug-ssrf-plugin": "^2.0",
|
||||||
"jawira/case-converter": "^3.4",
|
"jawira/case-converter": "^3.4",
|
||||||
"jean85/pretty-package-versions": "^1.3",
|
"jean85/pretty-package-versions": "^1.3",
|
||||||
"mathmarques/smarty-view": "^1.1",
|
"mathmarques/smarty-view": "^1.2",
|
||||||
"oomphinc/composer-installers-extender": "^2.0",
|
"oomphinc/composer-installers-extender": "^2.0",
|
||||||
"paragonie/csp-builder": "^2.5",
|
"paragonie/csp-builder": "^2.5",
|
||||||
"rinvex/countries": "^6.1",
|
"rinvex/countries": "^7.3",
|
||||||
"rudloff/alltube-library": "^0.1.1",
|
"rudloff/alltube-library": "^0.1.3",
|
||||||
"symfony/finder": "^5.0",
|
"symfony/finder": "^5.4",
|
||||||
"symfony/translation": "^4.0",
|
"symfony/translation": "^4.0",
|
||||||
"symfony/yaml": "^4.0",
|
"symfony/yaml": "^4.0",
|
||||||
"webfontkit/open-sans": "^1.0",
|
"webfontkit/open-sans": "^1.0",
|
||||||
"ytdl-org/youtube-dl": "2020.11.01.1",
|
"yt-dlp/yt-dlp": "^2023.03",
|
||||||
"zonuexe/http-accept-language": "^0.4.1"
|
"zonuexe/http-accept-language": "^0.4.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"consolidation/robo": "^2.1",
|
"consolidation/robo": "^3.0",
|
||||||
"ergebnis/composer-normalize": "^2.6",
|
"enlightn/security-checker": "^1.4",
|
||||||
"insite/composer-dangling-locked-deps": "^0.2.0",
|
"ergebnis/composer-normalize": "^2.20",
|
||||||
|
"insite/composer-dangling-locked-deps": "^0.2.1",
|
||||||
|
"junker/debugbar-smarty": "^0.1.0",
|
||||||
|
"kitchenu/slim-debugbar": "^1.1",
|
||||||
|
"maximebf/debugbar": "^1.16",
|
||||||
"php-mock/php-mock-mockery": "^1.3",
|
"php-mock/php-mock-mockery": "^1.3",
|
||||||
"phpro/grumphp": "^1.0",
|
"phpro/grumphp": "^1.3",
|
||||||
"phpstan/phpstan": "^0.12.25",
|
"phpstan/phpstan": "^0.12.72",
|
||||||
"phpunit/phpunit": "^8.4",
|
"phpunit/phpunit": "^9.5",
|
||||||
"sensiolabs/security-checker": "^6.0",
|
"povils/phpmnd": "^2.5",
|
||||||
"smarty-gettext/smarty-gettext": "^1.6",
|
"smarty-gettext/smarty-gettext": "^1.6",
|
||||||
"squizlabs/php_codesniffer": "^3.5",
|
"squizlabs/php_codesniffer": "^3.5",
|
||||||
"symfony/error-handler": "^5.0",
|
"symfony/error-handler": "^5.4",
|
||||||
"symfony/var-dumper": "^5.0"
|
"symfony/var-dumper": "^5.4"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": {
|
||||||
|
"name": "yt-dlp/yt-dlp",
|
||||||
|
"version": "2023.03.04",
|
||||||
|
"dist": {
|
||||||
|
"type": "tar",
|
||||||
|
"url": "https://github.com/yt-dlp/yt-dlp/releases/download/2023.03.04/yt-dlp.tar.gz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Alltube\\": "classes/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Alltube\\Test\\": "tests/"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"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,
|
||||||
|
"php-http/discovery": true,
|
||||||
|
"phpro/grumphp": true
|
||||||
|
},
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "7.3.11"
|
"php": "7.4.33"
|
||||||
},
|
},
|
||||||
"sort-packages": true
|
"sort-packages": true
|
||||||
},
|
},
|
||||||
|
@ -70,31 +107,9 @@
|
||||||
},
|
},
|
||||||
"installer-types": [
|
"installer-types": [
|
||||||
"library"
|
"library"
|
||||||
]
|
],
|
||||||
|
"patches": {}
|
||||||
},
|
},
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Alltube\\": "classes/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"Alltube\\Test\\": "tests/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repositories": [
|
|
||||||
{
|
|
||||||
"type": "package",
|
|
||||||
"package": {
|
|
||||||
"name": "ytdl-org/youtube-dl",
|
|
||||||
"version": "2020.11.01.1",
|
|
||||||
"dist": {
|
|
||||||
"type": "tar",
|
|
||||||
"url": "https://files.pythonhosted.org/packages/2d/70/bd9ec7f14efab2811fc4bd39cd586ed5e292547552310ee863ff84fa7791/youtube_dl-2020.11.1.1.tar.gz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "grumphp run --ansi",
|
"lint": "grumphp run --ansi",
|
||||||
"release": "robo release --ansi",
|
"release": "robo release --ansi",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
# Path to your youtube-dl binary
|
# Path to your youtube-dl binary
|
||||||
youtubedl: vendor/ytdl-org/youtube-dl/youtube_dl/__main__.py
|
youtubedl: vendor/yt-dlp/yt-dlp/yt_dlp/__main__.py
|
||||||
|
|
||||||
# Path to your python binary
|
# Path to your python binary
|
||||||
python: /usr/bin/python
|
python: /usr/bin/python
|
||||||
|
@ -12,6 +12,8 @@ params:
|
||||||
- --flat-playlist
|
- --flat-playlist
|
||||||
- --restrict-filenames
|
- --restrict-filenames
|
||||||
- --no-playlist
|
- --no-playlist
|
||||||
|
- --use-extractors
|
||||||
|
- default,-generic
|
||||||
|
|
||||||
# True to enable audio conversion
|
# True to enable audio conversion
|
||||||
convert: false
|
convert: false
|
||||||
|
|
|
@ -255,7 +255,7 @@ footer a:hover {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 622px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mp3-inner {
|
.mp3-inner {
|
||||||
|
@ -545,6 +545,7 @@ h1 {
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.format {
|
.format {
|
||||||
|
|
|
@ -10,9 +10,10 @@ grumphp:
|
||||||
xmllint: ~
|
xmllint: ~
|
||||||
yamllint: ~
|
yamllint: ~
|
||||||
composer: ~
|
composer: ~
|
||||||
securitychecker: ~
|
securitychecker_enlightn: ~
|
||||||
composer_normalize: ~
|
composer_normalize: ~
|
||||||
composer_dangling_locked_deps: ~
|
composer_dangling_locked_deps: ~
|
||||||
|
phpmnd: ~
|
||||||
phpcs:
|
phpcs:
|
||||||
standard: PSR12
|
standard: PSR12
|
||||||
phpstan:
|
phpstan:
|
||||||
|
|
|
@ -98,7 +98,7 @@ msgid "Share on Facebook"
|
||||||
msgstr "شاركها على فيسبوك"
|
msgstr "شاركها على فيسبوك"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "انسخ هنا رابط الفيديو (يوتيوب، انستقرام، وغيرها)"
|
msgstr "انسخ هنا رابط الفيديو (يوتيوب، انستقرام، وغيرها)"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
|
|
|
@ -131,8 +131,8 @@ msgid "Video password"
|
||||||
msgstr "Videopasswort"
|
msgstr "Videopasswort"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Kopiere hier die URL deines Videos (Youtube, Dailymotion, etc.) hinein"
|
msgstr "Kopiere hier die URL deines Videos (YouTube, Dailymotion, etc.) hinein"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
||||||
msgstr "Compartir en Facebook"
|
msgstr "Compartir en Facebook"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Copia aquí la URL de tu vídeo (Youtube, Dailymotion, etc.)"
|
msgstr "Copia aquí la URL de tu vídeo (YouTube, Dailymotion, etc.)"
|
||||||
|
|
||||||
#: templates/index.tpl:23
|
#: templates/index.tpl:23
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -69,8 +69,8 @@ msgid "Video password"
|
||||||
msgstr "Mot de passe de la vidéo"
|
msgstr "Mot de passe de la vidéo"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Copiez ici l'URL de votre vidéo (Youtube, Dailymotion, etc.)"
|
msgstr "Copiez ici l'URL de votre vidéo (YouTube, Dailymotion, etc.)"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
@ -219,17 +219,17 @@ msgstr "Impossible de trouver l'URL de la vidéo."
|
||||||
|
|
||||||
#: controllers/FrontController.php:64
|
#: controllers/FrontController.php:64
|
||||||
msgid ""
|
msgid ""
|
||||||
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
|
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Téléchargez facilement des vidéos depuis Youtube, Dailymotion, Vimeo et "
|
"Téléchargez facilement des vidéos depuis YouTube, Dailymotion, Vimeo et "
|
||||||
"d'autres sites web."
|
"d'autres sites web."
|
||||||
|
|
||||||
#: controllers/FrontController.php:110
|
#: controllers/FrontController.php:110
|
||||||
msgid ""
|
msgid ""
|
||||||
"List of all supported websites from which Alltube Download can extract video "
|
"List of all supported websites from which AllTube Download can extract video "
|
||||||
"or audio files"
|
"or audio files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Liste de tous les sites web depuis lesquels Alltube Download peut extraire "
|
"Liste de tous les sites web depuis lesquels AllTube Download peut extraire "
|
||||||
"des fichiers vidéo ou audio"
|
"des fichiers vidéo ou audio"
|
||||||
|
|
||||||
#: controllers/FrontController.php:136
|
#: controllers/FrontController.php:136
|
||||||
|
@ -238,9 +238,9 @@ msgstr "Demande de mot de passe"
|
||||||
|
|
||||||
#: controllers/FrontController.php:138
|
#: controllers/FrontController.php:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"You need a password in order to download this video with Alltube Download"
|
"You need a password in order to download this video with AllTube Download"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Vous avez besoin d'un mot de passe pour télécharger cette vidéo avec Alltube "
|
"Vous avez besoin d'un mot de passe pour télécharger cette vidéo avec AllTube "
|
||||||
"Download"
|
"Download"
|
||||||
|
|
||||||
#: controllers/FrontController.php:169
|
#: controllers/FrontController.php:169
|
||||||
|
|
|
@ -59,8 +59,8 @@ msgid "Video password"
|
||||||
msgstr "Password del video"
|
msgstr "Password del video"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Copia qui l'URL del video (Youtube, Dailymotion, ecc.)"
|
msgstr "Copia qui l'URL del video (YouTube, Dailymotion, ecc.)"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: POEditor.com\n"
|
||||||
|
"Project-Id-Version: Alltube Download\n"
|
||||||
|
"Language: ja-JP\n"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:8
|
||||||
|
msgid "Code by @dev"
|
||||||
|
msgstr "作成: @dev"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:16
|
||||||
|
msgid "Design by @designer"
|
||||||
|
msgstr "デザイン: @designer"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:21
|
||||||
|
msgid "Get the code"
|
||||||
|
msgstr "プログラムをダウンロード"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:29
|
||||||
|
msgid "Based on @youtubedl"
|
||||||
|
msgstr "本ソフトは@youtubedlを基に構成されています。"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:33
|
||||||
|
msgid "Donate using Liberapay"
|
||||||
|
msgstr "Liberapayで寄付"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:35
|
||||||
|
msgid "Donate"
|
||||||
|
msgstr "寄付"
|
||||||
|
|
||||||
|
#: templates/inc/header.tpl:4
|
||||||
|
msgid "Switch language"
|
||||||
|
msgstr "言語の変更"
|
||||||
|
|
||||||
|
#: templates/inc/header.tpl:8
|
||||||
|
msgid "Set language"
|
||||||
|
msgstr "言語を設定"
|
||||||
|
|
||||||
|
#: templates/info.tpl:11
|
||||||
|
msgid "You are going to download @title."
|
||||||
|
msgstr "ダウンロード対象: @title"
|
||||||
|
|
||||||
|
#: templates/info.tpl:29
|
||||||
|
msgid "Available formats:"
|
||||||
|
msgstr "ダウンロード可能な形式"
|
||||||
|
|
||||||
|
#: templates/info.tpl:31
|
||||||
|
msgid "Generic formats"
|
||||||
|
msgstr "よく使われる形式"
|
||||||
|
|
||||||
|
#: templates/info.tpl:35
|
||||||
|
msgid "Best"
|
||||||
|
msgstr "最高品質"
|
||||||
|
|
||||||
|
#: templates/info.tpl:36
|
||||||
|
msgid "Remux best video with best audio"
|
||||||
|
msgstr "最高品質に再エンコード"
|
||||||
|
|
||||||
|
#: templates/info.tpl:37
|
||||||
|
msgid "Worst"
|
||||||
|
msgstr "最低品質"
|
||||||
|
|
||||||
|
#: templates/info.tpl:42
|
||||||
|
msgid "Detailed formats"
|
||||||
|
msgstr "その他の形式"
|
||||||
|
|
||||||
|
#: templates/info.tpl:86
|
||||||
|
msgid "Stream the video through the server"
|
||||||
|
msgstr "ビデオをサーバーに設置してダウンロード"
|
||||||
|
|
||||||
|
#: templates/info.tpl:92
|
||||||
|
msgid "Convert into a custom format:"
|
||||||
|
msgstr "指定した形式への変換"
|
||||||
|
|
||||||
|
#: templates/info.tpl:93
|
||||||
|
msgid "Custom format"
|
||||||
|
msgstr "カスタム形式"
|
||||||
|
|
||||||
|
#: templates/info.tpl:93
|
||||||
|
msgid "Format to convert to"
|
||||||
|
msgstr "ファイル形式の変換"
|
||||||
|
|
||||||
|
#: templates/info.tpl:98
|
||||||
|
msgid "with"
|
||||||
|
msgstr "と"
|
||||||
|
|
||||||
|
#: templates/info.tpl:99
|
||||||
|
msgid "Bit rate"
|
||||||
|
msgstr "ビットレート"
|
||||||
|
|
||||||
|
#: templates/info.tpl:100
|
||||||
|
msgid "Custom bitrate"
|
||||||
|
msgstr "カスタム・ビットレート"
|
||||||
|
|
||||||
|
#: templates/info.tpl:103
|
||||||
|
msgid "kbit/s audio"
|
||||||
|
msgstr "kbps 音声"
|
||||||
|
|
||||||
|
#: templates/info.tpl:107 templates/playlist.tpl:38 templates/password.tpl:11
|
||||||
|
#: templates/index.tpl:19
|
||||||
|
msgid "Download"
|
||||||
|
msgstr "ダウンロード"
|
||||||
|
|
||||||
|
#: templates/playlist.tpl:12
|
||||||
|
msgid "Videos extracted from @title:"
|
||||||
|
msgstr "デコードの対象: @title"
|
||||||
|
|
||||||
|
#: templates/playlist.tpl:39
|
||||||
|
msgid "More options"
|
||||||
|
msgstr "詳細設定"
|
||||||
|
|
||||||
|
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:111
|
||||||
|
msgid "Supported websites"
|
||||||
|
msgstr "ダウンロードに対応しているサイト"
|
||||||
|
|
||||||
|
#: templates/error.tpl:5
|
||||||
|
msgid "An error occurred"
|
||||||
|
msgstr "予期せぬエラーが発生しました。"
|
||||||
|
|
||||||
|
#: templates/password.tpl:5
|
||||||
|
msgid "This video is protected"
|
||||||
|
msgstr "このビデオにはプロテクトがかかっています。"
|
||||||
|
|
||||||
|
#: templates/password.tpl:6
|
||||||
|
msgid "You need a password in order to download this video."
|
||||||
|
msgstr "このビデオをダウンロードするには閲覧用のパスワードが必要です。"
|
||||||
|
|
||||||
|
#: templates/password.tpl:8
|
||||||
|
msgid "Video password"
|
||||||
|
msgstr "閲覧用パスワード"
|
||||||
|
|
||||||
|
#: templates/index.tpl:8
|
||||||
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
|
msgstr "動画のリンク(URL)を入力欄に入力してください。(例:YouTube,Dailymotion等。)"
|
||||||
|
|
||||||
|
#: templates/index.tpl:25
|
||||||
|
msgid "Audio only (MP3)"
|
||||||
|
msgstr "音声のみのダウンロード(mp3形式)"
|
||||||
|
|
||||||
|
#: templates/index.tpl:29
|
||||||
|
msgid "From"
|
||||||
|
msgstr "から"
|
||||||
|
|
||||||
|
#: templates/index.tpl:32
|
||||||
|
msgid "to"
|
||||||
|
msgstr "へ"
|
||||||
|
|
||||||
|
#: templates/index.tpl:41
|
||||||
|
msgid "See all supported websites"
|
||||||
|
msgstr "ダウンロード可能なサイトを見る"
|
||||||
|
|
||||||
|
#: templates/index.tpl:43
|
||||||
|
msgid "Drag this to your bookmarks bar:"
|
||||||
|
msgstr "ブックマークバーにドラッグして登録"
|
||||||
|
|
||||||
|
#: templates/index.tpl:45
|
||||||
|
msgid "Bookmarklet"
|
||||||
|
msgstr "ブックマークボタン"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:64
|
||||||
|
#: classes/Controller/FrontController.php:166
|
||||||
|
msgid "Wrong password"
|
||||||
|
msgstr "パスワードが間違っています。"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:69
|
||||||
|
msgid "Conversion of playlists is not supported."
|
||||||
|
msgstr "プレイリストからの読み出しには対応しておりません。"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:76
|
||||||
|
msgid "Conversion of M3U8 files is not supported."
|
||||||
|
msgstr "HLS(m3u8)プレイリストファイルからの読み出しには対応しておりません。"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:82
|
||||||
|
msgid "Conversion of DASH segments is not supported."
|
||||||
|
msgstr "MPEG DASHストリーミングからの読み出しには対応しておりません。"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:65
|
||||||
|
msgid "Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||||
|
msgstr "Youtubeから動画を簡単にダウンロード!、DailymotionやVimeo等にも対応しております"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:112
|
||||||
|
msgid "List of all supported websites from which AllTube Download can extract video or audio files"
|
||||||
|
msgstr "AllTube上でのダウンロードおよびファイルの変換に対応している音声または動画ファイルのサイト"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:138
|
||||||
|
msgid "Password prompt"
|
||||||
|
msgstr "パスワード画面"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:140
|
||||||
|
msgid "You need a password in order to download this video with AllTube Download"
|
||||||
|
msgstr "このビデオをAllTubeでダウンロードするにはパスワードが必要です。"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:174
|
||||||
|
msgid "Video download"
|
||||||
|
msgstr "動画をダウンロード"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:176
|
||||||
|
msgid "Download video from @extractor"
|
||||||
|
msgstr "@extractor からダウンロードします。"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:182
|
||||||
|
msgid "Download @title from @extractor"
|
||||||
|
msgstr "@extractor から @title をダウンロードします"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:255
|
||||||
|
msgid "Error"
|
||||||
|
msgstr "エラーが発生しました。"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:271
|
||||||
|
msgid "Page not found"
|
||||||
|
msgstr "存在しないページです。"
|
||||||
|
|
||||||
|
#: classes/Controller/FrontController.php:282
|
||||||
|
msgid "Method not allowed"
|
||||||
|
msgstr "無効なリクエストです。"
|
|
@ -71,8 +71,8 @@ msgid "Video password"
|
||||||
msgstr "Hasło do wideo"
|
msgstr "Hasło do wideo"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Zamieść link do wideo (Yotube, Dailymotion, itp.)"
|
msgstr "Zamieść link do wideo (YouTube, Dailymotion, itp.)"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
||||||
msgstr "Compartilhe no Facebook"
|
msgstr "Compartilhe no Facebook"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Cole aqui a URL do vídeo (Youtube, Dailymotion, etc.)"
|
msgstr "Cole aqui a URL do vídeo (YouTube, Dailymotion, etc.)"
|
||||||
|
|
||||||
#: templates/index.tpl:24
|
#: templates/index.tpl:24
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Poedit 2.0.1\n"
|
||||||
|
"Last-Translator: progit <pash.vld@gmail.com>\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||||
|
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"Language: ru_RU\n"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:8
|
||||||
|
msgid "Code by @dev"
|
||||||
|
msgstr "Код @dev"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:16
|
||||||
|
msgid "Design by @designer"
|
||||||
|
msgstr "Дизайн @designer"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:21
|
||||||
|
msgid "Get the code"
|
||||||
|
msgstr "Получить код"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:29
|
||||||
|
msgid "Based on @youtubedl"
|
||||||
|
msgstr "На основе @youtubedl"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:33
|
||||||
|
msgid "Donate using Liberapay"
|
||||||
|
msgstr "Пожертвовать с помощью Liberapay"
|
||||||
|
|
||||||
|
#: templates/inc/footer.tpl:35
|
||||||
|
msgid "Donate"
|
||||||
|
msgstr "Пожертвовать"
|
||||||
|
|
||||||
|
#: templates/inc/header.tpl:4
|
||||||
|
msgid "Switch language"
|
||||||
|
msgstr "Смена языка"
|
||||||
|
|
||||||
|
#: templates/inc/header.tpl:8
|
||||||
|
msgid "Set language"
|
||||||
|
msgstr "Установить язык"
|
||||||
|
|
||||||
|
#: templates/info.tpl:11
|
||||||
|
msgid "You are going to download @title."
|
||||||
|
msgstr "Вы собираетесь скачать @title."
|
||||||
|
|
||||||
|
#: templates/info.tpl:29
|
||||||
|
msgid "Available formats:"
|
||||||
|
msgstr "Доступные форматы:"
|
||||||
|
|
||||||
|
#: templates/info.tpl:31
|
||||||
|
msgid "Generic formats"
|
||||||
|
msgstr "Общие форматы"
|
||||||
|
|
||||||
|
#: templates/info.tpl:35
|
||||||
|
msgid "Best"
|
||||||
|
msgstr "Лучшее качество"
|
||||||
|
|
||||||
|
#: templates/info.tpl:36
|
||||||
|
msgid "Remux best video with best audio"
|
||||||
|
msgstr "Перекодировать лучшее видео с лучшим звуком"
|
||||||
|
|
||||||
|
#: templates/info.tpl:37
|
||||||
|
msgid "Worst"
|
||||||
|
msgstr "Худшее качество"
|
||||||
|
|
||||||
|
#: templates/info.tpl:42
|
||||||
|
msgid "Detailed formats"
|
||||||
|
msgstr "Другие форматы"
|
||||||
|
|
||||||
|
#: templates/info.tpl:86
|
||||||
|
msgid "Stream the video through the server"
|
||||||
|
msgstr "Потоковое видео через сервер"
|
||||||
|
|
||||||
|
#: templates/info.tpl:92
|
||||||
|
msgid "Convert into a custom format:"
|
||||||
|
msgstr "Преобразовать в пользовательский формат:"
|
||||||
|
|
||||||
|
#: templates/info.tpl:93
|
||||||
|
msgid "Custom format"
|
||||||
|
msgstr "Пользовательский формат"
|
||||||
|
|
||||||
|
#: templates/info.tpl:93
|
||||||
|
msgid "Format to convert to"
|
||||||
|
msgstr "Формат для преобразования в"
|
||||||
|
|
||||||
|
#: templates/info.tpl:98
|
||||||
|
msgid "with"
|
||||||
|
msgstr "с"
|
||||||
|
|
||||||
|
#: templates/info.tpl:99
|
||||||
|
msgid "Bit rate"
|
||||||
|
msgstr "Битрейт"
|
||||||
|
|
||||||
|
#: templates/info.tpl:100
|
||||||
|
msgid "Custom bitrate"
|
||||||
|
msgstr "Пользовательский битрейт"
|
||||||
|
|
||||||
|
#: templates/info.tpl:103
|
||||||
|
msgid "kbit/s audio"
|
||||||
|
msgstr "кбит/с аудио"
|
||||||
|
|
||||||
|
#: templates/info.tpl:107 templates/playlist.tpl:38 templates/password.tpl:11
|
||||||
|
#: templates/index.tpl:19
|
||||||
|
msgid "Download"
|
||||||
|
msgstr "Скачать"
|
||||||
|
|
||||||
|
#: templates/playlist.tpl:12
|
||||||
|
msgid "Videos extracted from @title:"
|
||||||
|
msgstr "Извлеченные видео из @title:"
|
||||||
|
|
||||||
|
#: templates/playlist.tpl:39
|
||||||
|
msgid "More options"
|
||||||
|
msgstr "Больше вариантов"
|
||||||
|
|
||||||
|
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:111
|
||||||
|
msgid "Supported websites"
|
||||||
|
msgstr "Поддерживаемые сайты"
|
||||||
|
|
||||||
|
#: templates/error.tpl:5
|
||||||
|
msgid "An error occurred"
|
||||||
|
msgstr "Произошла ошибка"
|
||||||
|
|
||||||
|
#: templates/password.tpl:5
|
||||||
|
msgid "This video is protected"
|
||||||
|
msgstr "Это видео защищено"
|
||||||
|
|
||||||
|
#: templates/password.tpl:6
|
||||||
|
msgid "You need a password in order to download this video."
|
||||||
|
msgstr "Чтобы скачать это видео, вам нужен пароль."
|
||||||
|
|
||||||
|
#: templates/password.tpl:8
|
||||||
|
msgid "Video password"
|
||||||
|
msgstr "Пароль для видео"
|
||||||
|
|
||||||
|
#: templates/index.tpl:8
|
||||||
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
|
msgstr "Скопируйте сюда URL вашего видео (YouTube, Dailymotion и т. д.)"
|
||||||
|
|
||||||
|
#: templates/index.tpl:25
|
||||||
|
msgid "Audio only (MP3)"
|
||||||
|
msgstr "Только звук (MP3)"
|
||||||
|
|
||||||
|
#: templates/index.tpl:29
|
||||||
|
msgid "From"
|
||||||
|
msgstr "Из"
|
||||||
|
|
||||||
|
#: templates/index.tpl:32
|
||||||
|
msgid "to"
|
||||||
|
msgstr "в"
|
||||||
|
|
||||||
|
#: templates/index.tpl:41
|
||||||
|
msgid "See all supported websites"
|
||||||
|
msgstr "Просмотреть все поддерживаемые веб-сайты"
|
||||||
|
|
||||||
|
#: templates/index.tpl:43
|
||||||
|
msgid "Drag this to your bookmarks bar:"
|
||||||
|
msgstr "Перетащите это на панель закладок:"
|
||||||
|
|
||||||
|
#: templates/index.tpl:45
|
||||||
|
msgid "Bookmarklet"
|
||||||
|
msgstr "Букмарклет"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:64
|
||||||
|
#: classes/Controller/FrontController.php:166
|
||||||
|
msgid "Wrong password"
|
||||||
|
msgstr "Неправильный пароль"
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:69
|
||||||
|
msgid "Conversion of playlists is not supported."
|
||||||
|
msgstr "Конвертация плейлистов не поддерживается."
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:76
|
||||||
|
msgid "Conversion of M3U8 files is not supported."
|
||||||
|
msgstr "Конвертация файлов M3U8 не поддерживается."
|
||||||
|
|
||||||
|
#: classes/Controller/DownloadController.php:82
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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 "Скачать @title из @extractor"
|
||||||
|
|
||||||
|
#: 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 "Метод не разрешен"
|
|
@ -127,7 +127,7 @@ msgid "Video password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
|
@ -173,12 +173,12 @@ msgstr ""
|
||||||
|
|
||||||
#: classes/Controller/FrontController.php:65
|
#: classes/Controller/FrontController.php:65
|
||||||
msgid ""
|
msgid ""
|
||||||
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
|
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: classes/Controller/FrontController.php:112
|
#: classes/Controller/FrontController.php:112
|
||||||
msgid ""
|
msgid ""
|
||||||
"List of all supported websites from which Alltube Download can extract video "
|
"List of all supported websites from which AllTube Download can extract video "
|
||||||
"or audio files"
|
"or audio files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ msgstr ""
|
||||||
|
|
||||||
#: classes/Controller/FrontController.php:140
|
#: classes/Controller/FrontController.php:140
|
||||||
msgid ""
|
msgid ""
|
||||||
"You need a password in order to download this video with Alltube Download"
|
"You need a password in order to download this video with AllTube Download"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: classes/Controller/FrontController.php:174
|
#: classes/Controller/FrontController.php:174
|
||||||
|
|
|
@ -65,8 +65,8 @@ msgid "Video password"
|
||||||
msgstr "Video parolası"
|
msgstr "Video parolası"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/index.tpl:8
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||||
msgstr "Videonuzun URL'sini buraya kopyalayın (Youtube, Dailymotion, vb.)"
|
msgstr "Videonuzun URL'sini buraya kopyalayın (YouTube, Dailymotion, vb.)"
|
||||||
|
|
||||||
#: templates/index.tpl:25
|
#: templates/index.tpl:25
|
||||||
msgid "Audio only (MP3)"
|
msgid "Audio only (MP3)"
|
||||||
|
|
|
@ -1,134 +1,227 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"X-Generator: POEditor.com\n"
|
|
||||||
"Project-Id-Version: AllTube Download\n"
|
|
||||||
"Language: zh-CN\n"
|
|
||||||
|
|
||||||
#: templates/error.tpl:6
|
#: templates/inc/footer.tpl:8
|
||||||
msgid "Please check the URL of your video."
|
msgid "Code by @dev"
|
||||||
msgstr "请检查您的视频的 URL。"
|
msgstr "由 @dev 开发"
|
||||||
|
|
||||||
#: 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:16
|
#: templates/inc/footer.tpl:16
|
||||||
msgid "Based on"
|
msgid "Design by @designer"
|
||||||
msgstr "基于"
|
msgstr "由 @designer 设计"
|
||||||
|
|
||||||
#: templates/inc/header.tpl:21
|
#: templates/inc/footer.tpl:21
|
||||||
msgid "Share on Twitter"
|
msgid "Get the code"
|
||||||
msgstr "分享到 Twitter"
|
msgstr "获取源代码"
|
||||||
|
|
||||||
#: templates/inc/header.tpl:23
|
#: templates/inc/footer.tpl:29
|
||||||
msgid "Share on Facebook"
|
msgid "Based on @youtubedl"
|
||||||
msgstr "分享到 Facebook"
|
msgstr "基于 @youtubedl"
|
||||||
|
|
||||||
#: templates/index.tpl:8
|
#: templates/inc/footer.tpl:33
|
||||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
msgid "Donate using Liberapay"
|
||||||
msgstr "在这里复制您的视频 (Youtube、 Dailymotion 等) 的 URL"
|
msgstr "使用 Liberapay 捐赠"
|
||||||
|
|
||||||
#: templates/index.tpl:23
|
#: templates/inc/footer.tpl:35
|
||||||
msgid "Audio only (MP3)"
|
msgid "Donate"
|
||||||
msgstr "仅限音频(mp3)"
|
msgstr "捐赠"
|
||||||
|
|
||||||
#: 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/header.tpl:4
|
#: templates/inc/header.tpl:4
|
||||||
msgid "Switch language"
|
msgid "Switch language"
|
||||||
msgstr "切换语言"
|
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
|
#: templates/error.tpl:5
|
||||||
msgid "An error occurred"
|
msgid "An error occurred"
|
||||||
msgstr "出错了"
|
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 "不允许此请求方法"
|
||||||
|
|
|
@ -5,11 +5,6 @@ require_once __DIR__ . '/vendor/autoload.php';
|
||||||
use Alltube\App;
|
use Alltube\App;
|
||||||
use Alltube\ErrorHandler;
|
use Alltube\ErrorHandler;
|
||||||
|
|
||||||
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
|
|
||||||
header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
|
|
||||||
die;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create app.
|
// Create app.
|
||||||
$app = new App();
|
$app = new App();
|
||||||
|
|
15
phpunit.xml
15
phpunit.xml
|
@ -1,17 +1,14 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<phpunit bootstrap="tests/bootstrap.php">
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php"
|
||||||
<filter>
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
<whitelist>
|
<coverage>
|
||||||
|
<include>
|
||||||
<directory>classes/</directory>
|
<directory>classes/</directory>
|
||||||
</whitelist>
|
</include>
|
||||||
</filter>
|
</coverage>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Tests">
|
<testsuite name="Tests">
|
||||||
<directory>tests/</directory>
|
<directory>tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<logging>
|
|
||||||
<log type="coverage-html" target="coverage/"/>
|
|
||||||
<log type="coverage-clover" target="clover.xml"/>
|
|
||||||
</logging>
|
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -6,20 +6,6 @@ Most recent browsers automatically play a video
|
||||||
if it is a format they know how to play.
|
if it is a format they know how to play.
|
||||||
You can usually download the video by doing *File > Save to* or *ctrl + S*.
|
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 ressources
|
|
||||||
(although [donations are welcome](https://liberapay.com/Rudloff/))
|
|
||||||
and you are encouraged to host it yourself.
|
|
||||||
|
|
||||||
## alltubedownload.net often says "An error occurred in the application…"
|
|
||||||
|
|
||||||
See above.
|
|
||||||
|
|
||||||
## Change config parameters
|
## Change config parameters
|
||||||
|
|
||||||
You need to create a YAML file called `config.yml` in the `config/` folder.
|
You need to create a YAML file called `config.yml` in the `config/` folder.
|
||||||
|
@ -69,8 +55,7 @@ There are two known workarounds:
|
||||||
|
|
||||||
* You can run AllTube locally on your computer.
|
* You can run AllTube locally on your computer.
|
||||||
* You can enable streaming videos through the server (see below).
|
* You can enable streaming videos through the server (see below).
|
||||||
Please note that this can use a lot of resources on the server
|
Please note that this can use a lot of resources on the server.
|
||||||
(which is why we won't enable it on alltubedownload.net).
|
|
||||||
|
|
||||||
## I get a 404 error on every page except the index
|
## I get a 404 error on every page except the index
|
||||||
|
|
||||||
|
@ -91,7 +76,7 @@ You need to add this to your `config.yml` file:
|
||||||
stream: true
|
stream: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this can use a lot of ressources on your server.
|
Note that this can use a lot of resources on your server.
|
||||||
|
|
||||||
## Download M3U videos
|
## Download M3U videos
|
||||||
|
|
||||||
|
@ -129,7 +114,7 @@ remux: true
|
||||||
## Convert videos to something other than MP3
|
## Convert videos to something other than MP3
|
||||||
|
|
||||||
By default the `convert` option only allows converting to MP3,
|
By default the `convert` option only allows converting to MP3,
|
||||||
in order to keep things simple and ressources usage low.
|
in order to keep things simple and resources usage low.
|
||||||
However, you can use the `convertAdvanced` option like this:
|
However, you can use the `convertAdvanced` option like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
"start_url": "./",
|
"start_url": "../",
|
||||||
"theme_color": "#4F4F4F",
|
"theme_color": "#4F4F4F",
|
||||||
"background_color": "#EBEBEB",
|
"background_color": "#EBEBEB",
|
||||||
"orientation": "portrait"
|
"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'}
|
{extends file='page.tpl'}
|
||||||
<div class="wrapper">
|
{block name='main'}
|
||||||
<main class="main error">
|
<div class="error">
|
||||||
{include file="inc/logo.tpl"}
|
{include file="inc/logo.tpl"}
|
||||||
<h2>{t}An error occurred{/t}</h2>
|
<h2>{t}An error occurred{/t}</h2>
|
||||||
<p><i>{$error|escape|nl2br}</i></p>
|
<p><i>{$error|escape|nl2br}</i></p>
|
||||||
</main>
|
</div>
|
||||||
{include file='inc/footer.tpl'}
|
{/block}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{include file='inc/head.tpl'}
|
{extends file='page.tpl'}
|
||||||
{include file='inc/header.tpl'}
|
{block name='main'}
|
||||||
{include file='inc/logo.tpl'}
|
{include file='inc/logo.tpl'}
|
||||||
<h2 class="titre">{t}Supported websites{/t}</h2>
|
<h2 class="titre">{t}Supported websites{/t}</h2>
|
||||||
<div class="tripleliste">
|
<div class="tripleliste">
|
||||||
<ul>
|
<ul>
|
||||||
{foreach $extractors as $extractor}
|
{foreach $extractors as $extractor}
|
||||||
<li>{$extractor}</li>
|
<li>{$extractor}</li>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{include file='inc/footer.tpl'}
|
{/block}
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
</div>
|
|
||||||
<footer class="small-font">
|
<footer class="small-font">
|
||||||
<div class="footer_wrapper">
|
<div class="footer_wrapper">
|
||||||
{$dev="<a rel='author' target='blank'
|
{include file='snippets/dev.tpl' assign=dev}
|
||||||
href='http://rudloff.pro/'>
|
|
||||||
Pierre Rudloff
|
|
||||||
</a>"}
|
|
||||||
{t params=['@dev'=>$dev]}Code by @dev{/t}
|
{t params=['@dev'=>$dev]}Code by @dev{/t}
|
||||||
|
|
||||||
·
|
·
|
||||||
|
|
||||||
{$designer="<a rel='author' target='blank'
|
{include file='snippets/designer.tpl' assign=designer}
|
||||||
href='http://olivierhaquette.fr'>
|
|
||||||
Olivier Haquette
|
|
||||||
</a>"}
|
|
||||||
{t params=['@designer' => $designer]}Design by @designer{/t}
|
{t params=['@designer' => $designer]}Design by @designer{/t}
|
||||||
|
|
||||||
·
|
·
|
||||||
|
@ -23,19 +16,7 @@
|
||||||
|
|
||||||
·
|
·
|
||||||
|
|
||||||
{$youtubedl="<a href='http://ytdl-org.github.io/youtube-dl/'>
|
{include file='snippets/youtubedl.tpl' assign=youtubedl}
|
||||||
youtube-dl
|
|
||||||
</a>"}
|
|
||||||
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t}
|
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t}
|
||||||
|
|
||||||
·
|
|
||||||
|
|
||||||
<a rel="noopener" target="_blank" title="{t}Donate using Liberapay{/t}"
|
|
||||||
href="https://liberapay.com/Rudloff/donate">
|
|
||||||
{t}Donate{/t}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
<!doctype html>
|
|
||||||
<html {if isset($locale)}lang="{$locale->getBcp47()}"{/if}>
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta name=viewport content="width=device-width, initial-scale=1"/>
|
<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}/assets/open-sans/open-sans.css"/>
|
||||||
<link rel="stylesheet" href="{base_url}/css/style.css"/>
|
<link rel="stylesheet" href="{base_url}/css/style.css"/>
|
||||||
<title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title>
|
<title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title>
|
||||||
<link rel="canonical" href="{$canonical}"/>
|
|
||||||
<link rel="icon" href="{base_url}/img/favicon.png"/>
|
<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:title" content="{$config->appName}{if isset($title)} - {$title|escape}{/if}"/>
|
||||||
<meta property="og:image" content="{base_url}/img/logo.png"/>
|
<meta property="og:image" content="{base_url}/img/logo.png"/>
|
||||||
|
@ -22,6 +19,8 @@
|
||||||
<meta name="theme-color" content="#4F4F4F"/>
|
<meta name="theme-color" content="#4F4F4F"/>
|
||||||
<link rel="manifest" href="{base_url}/resources/manifest.json"/>
|
<link rel="manifest" href="{base_url}/resources/manifest.json"/>
|
||||||
<meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/>
|
<meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/>
|
||||||
|
|
||||||
|
{if isset($debug_render)}
|
||||||
|
{$debug_render->renderHead()}
|
||||||
|
{/if}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
<div class="page {$class}">
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
{if isset($supportedLocales) AND count($supportedLocales) > 1}
|
{if isset($supportedLocales) AND count($supportedLocales) > 1}
|
||||||
<div class="locales small-font">
|
<div class="locales small-font">
|
||||||
<button class="localesBtn small-font" title="{t}Switch language{/t}">
|
<button class="localesBtn small-font" title="{t}Switch language{/t}">
|
||||||
{if isset($locale) AND $locale->getCountry()}
|
{if $locale->getLocale()->getCountry()}
|
||||||
{$locale->getCountry()->getEmoji()}
|
{$locale->getLocale()->getCountry()->getEmoji()}
|
||||||
{else}
|
{else}
|
||||||
{t}Set language{/t}
|
{t}Set language{/t}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
{if $supportedLocale->getCountry()}
|
{if $supportedLocale->getCountry()}
|
||||||
{$supportedLocale->getCountry()->getEmoji()}
|
{$supportedLocale->getCountry()->getEmoji()}
|
||||||
{/if}
|
{/if}
|
||||||
{$supportedLocale->getFullName()|ucfirst}
|
{$supportedLocale->getFullName()}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -27,4 +27,3 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
<div class="wrapper">
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<h1 class="logobis">
|
<h1 class="logobis">
|
||||||
<a class="logocompatible" href="{base_url}">
|
<a class="logocompatible" href="{path_for name="index"}">
|
||||||
<span class="logocompatiblemask"><img src="{base_url}/img/logocompatiblemask.png" width="447" height="107"
|
<span class="logocompatiblemask">
|
||||||
alt="{$config->appName}"/></span>
|
{html_image file='img/logocompatiblemask.png' path_prefix={base_url}|cat:'/' alt=$config->appName}
|
||||||
</a></h1>
|
</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{include file='inc/head.tpl'}
|
{extends file='page.tpl'}
|
||||||
{include file='inc/header.tpl'}
|
{block name='main'}
|
||||||
<main class="main">
|
<div>
|
||||||
<div><img class="logo" src="{base_url}/img/logo.png"
|
{html_image file='img/logo.png' path_prefix={base_url}|cat:'/' alt=$config->appName class="logo"}
|
||||||
alt="{$config->appName}" width="328" height="284"></div>
|
</div>
|
||||||
<form action="{path_for name="info"}">
|
<form action="{path_for name="info"}">
|
||||||
<label class="labelurl" for="url">
|
<label class="labelurl" for="url">
|
||||||
{t}Copy here the URL of your video (Youtube, Dailymotion, etc.){/t}
|
{t}Copy here the URL of your video (YouTube, Dailymotion, etc.){/t}
|
||||||
</label>
|
</label>
|
||||||
<div class="champs">
|
<div class="champs">
|
||||||
<span class="URLinput_wrapper">
|
<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 -->
|
<!-- 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"
|
<input class="URLinput large-font" type="url" name="url" id="url"
|
||||||
required placeholder="http://example.com/video"/>
|
required placeholder="https://example.com/video"/>
|
||||||
</span>
|
</span>
|
||||||
{if $config->uglyUrls}
|
{if $config->uglyUrls}
|
||||||
<input type="hidden" name="page" value="info"/>
|
<input type="hidden" name="page" value="info"/>
|
||||||
|
@ -20,18 +20,23 @@
|
||||||
{if $config->convert}
|
{if $config->convert}
|
||||||
<div class="mp3 small-font">
|
<div class="mp3 small-font">
|
||||||
<div class="mp3-inner">
|
<div class="mp3-inner">
|
||||||
<input type="checkbox" id="audio" class="audio" name="audio" {($config->defaultAudio) ? 'checked' : ''}>
|
<input type="checkbox" id="audio" class="audio"
|
||||||
|
name="audio" {($config->defaultAudio) ? 'checked' : ''}>
|
||||||
<label for="audio"><span class="ui"></span>
|
<label for="audio"><span class="ui"></span>
|
||||||
{t}Audio only (MP3){/t}
|
{t}Audio only (MP3){/t}
|
||||||
</label>
|
</label>
|
||||||
{if $config->convertSeek}
|
{if $config->convertSeek}
|
||||||
<div class="seekOptions">
|
<div class="seekOptions">
|
||||||
<label for="from">{t}From{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
<label for="from">{t}From{/t}</label> <input type="text"
|
||||||
placeholder="HH:MM:SS" value="" name="from"
|
pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
||||||
id="from"/>
|
placeholder="HH:MM:SS" value=""
|
||||||
<label for="to">{t}to{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
name="from"
|
||||||
placeholder="HH:MM:SS" value="" name="to" id="to"/>
|
id="from"/>
|
||||||
</div>
|
<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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,6 +49,4 @@
|
||||||
<a class="bookmarklet small-font"
|
<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>
|
href="javascript:window.location='{$domain}{path_for name='info' queryParams=['url' => '%url%']}'.replace('%url%', encodeURIComponent(location.href));">{t}Bookmarklet{/t}</a>
|
||||||
</div>
|
</div>
|
||||||
|
{/block}
|
||||||
</main>
|
|
||||||
{include file='inc/footer.tpl'}
|
|
||||||
|
|
|
@ -1,111 +1,59 @@
|
||||||
{include file="inc/head.tpl"}
|
{extends file='page.tpl'}
|
||||||
<div class="wrapper">
|
{block name='main'}
|
||||||
<div itemscope itemtype="http://schema.org/VideoObject">
|
<div itemscope itemtype="https://schema.org/VideoObject">
|
||||||
<main class="main">
|
{include file="inc/logo.tpl"}
|
||||||
{include file="inc/logo.tpl"}
|
{include file='snippets/title.tpl' assign=title}
|
||||||
{$title="<i itemprop='name'>
|
<p id="download_intro">
|
||||||
<a itemprop='url' id='video_link'
|
{t params=['@title' => $title]}You are going to download @title.{/t}
|
||||||
href='{$video->webpage_url}'>
|
</p>
|
||||||
{$video->title}</a></i>"}
|
{if isset($video->thumbnail)}
|
||||||
<p id="download_intro">
|
{html_image file=$video->thumbnail itemprop="thumbnailUrl" class="thumb"}
|
||||||
{t params=['@title' => $title]}You are going to download @title.{/t}
|
{/if}
|
||||||
</p>
|
{if isset($video->description)}
|
||||||
{if isset($video->thumbnail)}
|
<meta itemprop="description" content="{$video->description|escape}"/>
|
||||||
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt=""/>
|
{/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}
|
||||||
{if isset($video->description)}
|
{if isset($video->formats) && count($video->formats) > 1}
|
||||||
<meta itemprop="description" content="{$video->description|escape}"/>
|
<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}
|
||||||
{if isset($video->upload_date)}
|
{if $config->stream}
|
||||||
<meta itemprop="uploadDate" content="{$video->upload_date}"/>
|
<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}
|
||||||
<br/>
|
{if $config->convertAdvanced}
|
||||||
<form action="{path_for name="download"}">
|
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||||
<input type="hidden" name="url" value="{$video->webpage_url}"/>
|
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||||
{if $config->uglyUrls}
|
{html_options name='customFormat' values=$config->convertAdvancedFormats output=$config->convertAdvancedFormats
|
||||||
<input type="hidden" name="page" value="download"/>
|
title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}"}
|
||||||
{/if}
|
{t}with{/t}
|
||||||
{if isset($video->formats) && count($video->formats) > 1}
|
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
|
||||||
<h3><label for="format">{t}Available formats:{/t}</label></h3>
|
<input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}"
|
||||||
<select name="format" id="format" class="formats monospace">
|
class="customBitrate"
|
||||||
<optgroup label="{t}Generic formats{/t}">
|
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit"/>
|
||||||
{foreach $config->genericFormats as $format => $name}
|
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
|
||||||
{*
|
<br/>
|
||||||
To make the default generic formats translatable:
|
<br/>
|
||||||
{t}Best{/t}
|
{/if}
|
||||||
{t}Remux best video with best audio{/t}
|
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/><br/>
|
||||||
{t}Worst{/t}
|
</form>
|
||||||
*}
|
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
{include file="inc/footer.tpl"}
|
{/block}
|
||||||
|
|
|
@ -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'}
|
{extends file='page.tpl'}
|
||||||
<div class="wrapper">
|
{block name='main'}
|
||||||
<main class="main">
|
{include file="inc/logo.tpl"}
|
||||||
{include file="inc/logo.tpl"}
|
<h2>{t}This video is protected{/t}</h2>
|
||||||
<h2>{t}This video is protected{/t}</h2>
|
<p>{t}You need a password in order to download this video.{/t}</p>
|
||||||
<p>{t}You need a password in order to download this video.{/t}</p>
|
<form action="" method="POST">
|
||||||
<form action="" method="POST">
|
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
||||||
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
<input class="URLinput" type="password" name="password" id="password"/>
|
||||||
<input class="URLinput" type="password" name="password" id="password"/>
|
<br/><br/>
|
||||||
<br/><br/>
|
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
||||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
</form>
|
||||||
</form>
|
{/block}
|
||||||
</main>
|
|
||||||
{include file='inc/footer.tpl'}
|
|
||||||
|
|
|
@ -1,44 +1,40 @@
|
||||||
{include file="inc/head.tpl"}
|
{extends file='page.tpl'}
|
||||||
<div class="wrapper">
|
{block name='main'}
|
||||||
<main class="main">
|
{include file="inc/logo.tpl"}
|
||||||
{include file="inc/logo.tpl"}
|
|
||||||
|
|
||||||
{if isset($video->title)}
|
{if isset($video->title)}
|
||||||
{$title="<i>
|
{include file='snippets/title.tpl' assign=title}
|
||||||
<a href='{$video->webpage_url}'>
|
<p>
|
||||||
{$video->title}</a>
|
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
||||||
</i>"}
|
</p>
|
||||||
<p>
|
{/if}
|
||||||
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $config->stream}
|
{if $config->stream}
|
||||||
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
||||||
{/if}
|
{/if}
|
||||||
{foreach $video->entries as $entry}
|
{foreach $video->entries as $entry}
|
||||||
<div class="playlist-entry">
|
<div class="playlist-entry">
|
||||||
<h3 class="playlist-entry-title"><a target="_blank" href="{strip}
|
<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)}
|
{if isset($entry->ie_key) and $entry->ie_key == Youtube and !filter_var($entry->url, FILTER_VALIDATE_URL)}
|
||||||
https://www.youtube.com/watch?v=
|
https://www.youtube.com/watch?v=
|
||||||
{/if}
|
{/if}
|
||||||
{$entry->url}
|
{$entry->url}
|
||||||
{/strip}">
|
{/strip}">
|
||||||
{if !isset($entry->title)}
|
{if !isset($entry->title)}
|
||||||
{if $entry->ie_key == YoutubePlaylist}
|
{if $entry->ie_key == YoutubePlaylist}
|
||||||
Playlist
|
Playlist
|
||||||
{else}
|
|
||||||
Video
|
|
||||||
{/if}
|
|
||||||
{else}
|
{else}
|
||||||
{$entry->title}
|
Video
|
||||||
{/if}
|
{/if}
|
||||||
</a></h3>
|
{else}
|
||||||
<a target="_blank" class="downloadBtn"
|
{$entry->title}
|
||||||
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
{/if}
|
||||||
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
</a>
|
||||||
</div>
|
</h3>
|
||||||
{/foreach}
|
<a target="_blank" class="downloadBtn"
|
||||||
|
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
||||||
</main>
|
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
||||||
{include file="inc/footer.tpl"}
|
</div>
|
||||||
|
{/foreach}
|
||||||
|
{/block}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<a rel="author" target="blank"
|
||||||
|
href="https://ographik.fr/">
|
||||||
|
Olivier Haquette
|
||||||
|
</a>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<a rel="author" target="blank"
|
||||||
|
href="https://rudloff.pro/">
|
||||||
|
Pierre Rudloff
|
||||||
|
</a>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<i itemprop="name">
|
||||||
|
<a itemprop="url" id="video_link"
|
||||||
|
href="{$video->webpage_url}">
|
||||||
|
{$video->title}</a>
|
||||||
|
</i>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<a href="https://ytdl-org.github.io/youtube-dl/">
|
||||||
|
youtube-dl
|
||||||
|
</a>
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
namespace Alltube\Test;
|
namespace Alltube\Test;
|
||||||
|
|
||||||
|
use OndraM\CiDetector\CiDetector;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use PHPUnit\Util\Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class used by every test.
|
* Abstract class used by every test.
|
||||||
|
@ -18,7 +20,7 @@ abstract class BaseTest extends TestCase
|
||||||
*
|
*
|
||||||
* @return string Path to file
|
* @return string Path to file
|
||||||
*/
|
*/
|
||||||
protected function getConfigFile()
|
protected function getConfigFile(): string
|
||||||
{
|
{
|
||||||
return __DIR__ . '/../config/config_test.yml';
|
return __DIR__ . '/../config/config_test.yml';
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,11 @@ abstract class BaseTest extends TestCase
|
||||||
*/
|
*/
|
||||||
protected function checkRequirements()
|
protected function checkRequirements()
|
||||||
{
|
{
|
||||||
$annotations = $this->getAnnotations();
|
$ciDetector = new CiDetector();
|
||||||
|
$annotations = Test::parseTestMethodAnnotations(
|
||||||
|
static::class,
|
||||||
|
$this->getName()
|
||||||
|
);
|
||||||
$requires = [];
|
$requires = [];
|
||||||
|
|
||||||
if (isset($annotations['class']['requires'])) {
|
if (isset($annotations['class']['requires'])) {
|
||||||
|
@ -48,7 +54,7 @@ abstract class BaseTest extends TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($requires as $require) {
|
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.');
|
$this->markTestSkipped('Do not run tests that download videos on CI.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ use Alltube\Exception\ConfigException;
|
||||||
*/
|
*/
|
||||||
class ConfigTest extends BaseTest
|
class ConfigTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the getInstance function.
|
* Test the getInstance function.
|
||||||
*
|
*
|
||||||
|
|
|
@ -40,6 +40,7 @@ abstract class ContainerTest extends BaseTest
|
||||||
$this->checkRequirements();
|
$this->checkRequirements();
|
||||||
|
|
||||||
$this->container = new Container(['environment' => Environment::mock()]);
|
$this->container = new Container(['environment' => Environment::mock()]);
|
||||||
|
$this->container['root_path'] = dirname(__DIR__);
|
||||||
$this->container['config'] = Config::fromFile($this->getConfigFile());
|
$this->container['config'] = Config::fromFile($this->getConfigFile());
|
||||||
$this->container['session'] = SessionFactory::create($this->container);
|
$this->container['session'] = SessionFactory::create($this->container);
|
||||||
$this->container['locale'] = LocaleManagerFactory::create($this->container);
|
$this->container['locale'] = LocaleManagerFactory::create($this->container);
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
namespace Alltube\Test;
|
namespace Alltube\Test;
|
||||||
|
|
||||||
|
use Alltube\Locale;
|
||||||
use Alltube\Controller\BaseController;
|
use Alltube\Controller\BaseController;
|
||||||
use Alltube\Controller\DownloadController;
|
use Alltube\Controller\DownloadController;
|
||||||
use Alltube\Controller\FrontController;
|
use Alltube\Controller\FrontController;
|
||||||
use Alltube\Exception\ConfigException;
|
use Alltube\Exception\ConfigException;
|
||||||
use Alltube\Exception\DependencyException;
|
use Alltube\Exception\DependencyException;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
|
use Slim\Views\Smarty;
|
||||||
use SmartyException;
|
use SmartyException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,6 +36,8 @@ abstract class ControllerTest extends ContainerTest
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->container->get('locale')->setLocale(new Locale('en_US'));
|
||||||
|
|
||||||
$frontController = new FrontController($this->container);
|
$frontController = new FrontController($this->container);
|
||||||
$downloadController = new DownloadController($this->container);
|
$downloadController = new DownloadController($this->container);
|
||||||
|
|
||||||
|
@ -49,6 +53,12 @@ abstract class ControllerTest extends ContainerTest
|
||||||
->setName('locale');
|
->setName('locale');
|
||||||
$router->map(['GET'], '/redirect', [$downloadController, 'download'])
|
$router->map(['GET'], '/redirect', [$downloadController, 'download'])
|
||||||
->setName('download');
|
->setName('download');
|
||||||
|
|
||||||
|
/** @var Smarty $view */
|
||||||
|
$view = $this->container->get('view');
|
||||||
|
|
||||||
|
// Make sure we start the tests without compiled templates.
|
||||||
|
$view->getSmarty()->clearCompiledTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +69,7 @@ abstract class ControllerTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return Response HTTP response
|
* @return Response HTTP response
|
||||||
*/
|
*/
|
||||||
protected function getRequestResult(string $request, array $params)
|
protected function getRequestResult(string $request, array $params): Response
|
||||||
{
|
{
|
||||||
return $this->controller->$request(
|
return $this->controller->$request(
|
||||||
$this->container->get('request')->withQueryParams($params),
|
$this->container->get('request')->withQueryParams($params),
|
||||||
|
|
|
@ -17,7 +17,6 @@ use SmartyException;
|
||||||
*/
|
*/
|
||||||
class ConvertedPlaylistArchiveStreamTest extends StreamTest
|
class ConvertedPlaylistArchiveStreamTest extends StreamTest
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare tests.
|
* Prepare tests.
|
||||||
*
|
*
|
||||||
|
|
|
@ -108,14 +108,6 @@ class DownloadControllerTest extends ControllerTest
|
||||||
public function testDownloadWithRtmpStream()
|
public function testDownloadWithRtmpStream()
|
||||||
{
|
{
|
||||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||||
|
|
||||||
$config = $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()
|
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()
|
public function testDownloadWithError()
|
||||||
{
|
{
|
||||||
$this->expectException(YoutubedlException::class);
|
$this->expectException(YoutubedlException::class);
|
||||||
$this->getRequestResult('download', ['url' => 'http://example.com/foo']);
|
$this->getRequestResult('download', ['url' => 'https://example.com/foo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,7 @@ use Alltube\Exception\ConfigException;
|
||||||
use Alltube\Exception\DependencyException;
|
use Alltube\Exception\DependencyException;
|
||||||
use Alltube\Library\Exception\AlltubeLibraryException;
|
use Alltube\Library\Exception\AlltubeLibraryException;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||||
use Slim\Http\Environment;
|
use Slim\Http\Environment;
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use SmartyException;
|
use SmartyException;
|
||||||
|
@ -113,7 +114,8 @@ class FrontControllerTest extends ControllerTest
|
||||||
*/
|
*/
|
||||||
public function testInfoWithoutUrl()
|
public function testInfoWithoutUrl()
|
||||||
{
|
{
|
||||||
$this->assertRequestIsRedirect('info');
|
$this->expectException(InvalidURLException::class);
|
||||||
|
$this->getRequestResult('info', []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,12 +186,12 @@ class FrontControllerTest extends ControllerTest
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @requires download
|
* @requires download
|
||||||
* @throws AlltubeLibraryException
|
* @throws AlltubeLibraryException|InvalidURLException
|
||||||
*/
|
*/
|
||||||
public function testInfoWithPassword()
|
public function testInfoWithPassword()
|
||||||
{
|
{
|
||||||
$result = $this->controller->info(
|
$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']),
|
->withParsedBody(['password' => 'youtube-dl']),
|
||||||
$this->container->get('response')
|
$this->container->get('response')
|
||||||
);
|
);
|
||||||
|
@ -204,8 +206,8 @@ class FrontControllerTest extends ControllerTest
|
||||||
*/
|
*/
|
||||||
public function testInfoWithMissingPassword()
|
public function testInfoWithMissingPassword()
|
||||||
{
|
{
|
||||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962']);
|
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962']);
|
||||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]);
|
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962', 'audio' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,7 +49,7 @@ class JsonControllerTest extends ControllerTest
|
||||||
public function testJsonWithError()
|
public function testJsonWithError()
|
||||||
{
|
{
|
||||||
$this->expectException(YoutubedlException::class);
|
$this->expectException(YoutubedlException::class);
|
||||||
$this->getRequestResult('json', ['url' => 'http://example.com/foo']);
|
$this->getRequestResult('json', ['url' => 'https://example.com/foo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -85,26 +85,32 @@ class LocaleMiddlewareTest extends ContainerTest
|
||||||
* Check that the request contains an Accept-Language header.
|
* Check that the request contains an Accept-Language header.
|
||||||
*
|
*
|
||||||
* @param Request $request PSR-7 request
|
* @param Request $request PSR-7 request
|
||||||
|
* @param Response $response
|
||||||
*
|
*
|
||||||
* @return void
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function assertHeader(Request $request)
|
public function assertHeader(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$header = $request->getHeader('Accept-Language');
|
$header = $request->getHeader('Accept-Language');
|
||||||
$this->assertEquals('foo-BAR', $header[0]);
|
$this->assertEquals('foo-BAR', $header[0]);
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the request contains no Accept-Language header.
|
* Check that the request contains no Accept-Language header.
|
||||||
*
|
*
|
||||||
* @param Request $request PSR-7 request
|
* @param Request $request PSR-7 request
|
||||||
|
* @param Response $response
|
||||||
*
|
*
|
||||||
* @return void
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function assertNoHeader(Request $request)
|
public function assertNoHeader(Request $request, Response $response): Response
|
||||||
{
|
{
|
||||||
$header = $request->getHeader('Accept-Language');
|
$header = $request->getHeader('Accept-Language');
|
||||||
$this->assertEmpty($header);
|
$this->assertEmpty($header);
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -54,7 +54,7 @@ class LocaleTest extends ContainerTest
|
||||||
*/
|
*/
|
||||||
public function testGetFullName()
|
public function testGetFullName()
|
||||||
{
|
{
|
||||||
$this->assertEquals('français (France)', $this->localeObject->getFullName());
|
$this->assertEquals('Français (France)', $this->localeObject->getFullName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,7 +37,16 @@ class UglyRouterTest extends ContainerTest
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->router = new UglyRouter();
|
$this->router = new UglyRouter();
|
||||||
$this->router->map(['GET'], '/foo', 'print')->setName('foo');
|
$this->router->map(['GET'], '/foo', [$this, 'fakeHandler'])->setName('foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty function that only exists so that our route can have a handler.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function fakeHandler()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +63,7 @@ class UglyRouterTest extends ContainerTest
|
||||||
Environment::mock(
|
Environment::mock(
|
||||||
[
|
[
|
||||||
'REQUEST_METHOD' => 'GET',
|
'REQUEST_METHOD' => 'GET',
|
||||||
'QUERY_STRING' => 'page=foo',
|
'QUERY_STRING' => 'page=foo',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -70,7 +79,7 @@ class UglyRouterTest extends ContainerTest
|
||||||
public function testPathFor()
|
public function testPathFor()
|
||||||
{
|
{
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'/?page=foo',
|
'/?page=%2Ffoo',
|
||||||
$this->router->pathFor('foo', [], [])
|
$this->router->pathFor('foo', [], [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +93,7 @@ class UglyRouterTest extends ContainerTest
|
||||||
{
|
{
|
||||||
$this->router->setBasePath('/bar');
|
$this->router->setBasePath('/bar');
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'/bar/?page=foo',
|
'/bar/?page=%2Ffoo',
|
||||||
$this->router->pathFor('foo', [], [])
|
$this->router->pathFor('foo', [], [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ class VideoTest extends ContainerTest
|
||||||
*/
|
*/
|
||||||
public function testgetUrlWithPassword()
|
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) {
|
foreach ($video->getUrl() as $videoURL) {
|
||||||
$this->assertStringContainsString('vimeocdn.com', $videoURL);
|
$this->assertStringContainsString('vimeocdn.com', $videoURL);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ class VideoTest extends ContainerTest
|
||||||
public function testgetUrlWithMissingPassword()
|
public function testgetUrlWithMissingPassword()
|
||||||
{
|
{
|
||||||
$this->expectException(PasswordException::class);
|
$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();
|
$video->getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class VideoTest extends ContainerTest
|
||||||
public function testgetUrlWithWrongPassword()
|
public function testgetUrlWithWrongPassword()
|
||||||
{
|
{
|
||||||
$this->expectException(WrongPasswordException::class);
|
$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();
|
$video->getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class VideoTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
public function urlProvider()
|
public function urlProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
@ -174,7 +174,7 @@ class VideoTest extends ContainerTest
|
||||||
'googlevideo.com',
|
'googlevideo.com',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
'https://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
||||||
'Kaleidoscope_Leonard_Cohen-b039d07m',
|
'Kaleidoscope_Leonard_Cohen-b039d07m',
|
||||||
'flv',
|
'flv',
|
||||||
'bbcodspdns.fcod.llnwd.net',
|
'bbcodspdns.fcod.llnwd.net',
|
||||||
|
@ -193,7 +193,7 @@ class VideoTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
public function remuxUrlProvider()
|
public function remuxUrlProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
@ -210,7 +210,7 @@ class VideoTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
public function m3uUrlProvider()
|
public function m3uUrlProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
@ -227,7 +227,7 @@ class VideoTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
public function rtmpUrlProvider()
|
public function rtmpUrlProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
@ -244,10 +244,10 @@ class VideoTest extends ContainerTest
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
*/
|
*/
|
||||||
public function errorUrlProvider()
|
public function errorUrlProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['http://example.com/video'],
|
['https://example.com/video'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,16 +479,11 @@ class VideoTest extends ContainerTest
|
||||||
* @param string $format Format
|
* @param string $format Format
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @throws AlltubeLibraryException
|
|
||||||
* @dataProvider rtmpUrlProvider
|
* @dataProvider rtmpUrlProvider
|
||||||
*/
|
*/
|
||||||
public function testGetRtmpStream(string $url, string $format)
|
public function testGetRtmpStream(string $url, string $format)
|
||||||
{
|
{
|
||||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||||
|
|
||||||
$video = new Video($this->downloader, $url, $format);
|
|
||||||
|
|
||||||
$this->assertStream($this->downloader->getRtmpStream($video));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue