mirror of https://github.com/Rudloff/alltube.git
Compare commits
137 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 | |
Pierre Rudloff | f6d3a72c31 | |
Pierre Rudloff | c0b2acf33e | |
Pierre Rudloff | 17422d8485 | |
Pierre Rudloff | b9c860f824 | |
Pierre Rudloff | f4a9528b56 | |
Pierre Rudloff | 02df3d219a |
|
@ -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>
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{HTTP_HOST} ^alltube\.herokuapp\.com$ [NC]
|
||||
RewriteRule ^(.*)$ https://www.alltubedownload.net/$1 [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [QSA,L]
|
||||
</ifmodule>
|
||||
|
|
12
.travis.yml
12
.travis.yml
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
language: php
|
||||
php: 7.3
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- language-pack-fr
|
||||
install: composer install --no-progress
|
||||
script:
|
||||
- composer check-platform-reqs
|
||||
- composer lint
|
||||
- composer test
|
|
@ -1,7 +1,6 @@
|
|||
FROM php:7.3-apache
|
||||
FROM php:7.4-apache
|
||||
RUN apt-get update
|
||||
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 gmp
|
||||
RUN a2enmod rewrite
|
||||
|
@ -10,4 +9,7 @@ COPY resources/php.ini /usr/local/etc/php/
|
|||
COPY . /var/www/html/
|
||||
RUN php composer.phar check-platform-reqs --no-dev
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
[![Donate using Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Rudloff/donate)
|
||||
|
||||
HTML GUI for youtube-dl ([alltubedownload.net](http://alltubedownload.net/))
|
||||
HTML GUI for youtube-dl
|
||||
|
||||
![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-Path (ex: `/alltube`)
|
||||
* X-Forwarded-Port (ex: `5555`)
|
||||
* X-Forwarded-Proto (ex: `https`)
|
||||
|
||||
### Apache
|
||||
|
||||
|
@ -166,7 +165,7 @@ so that you can reuse it in your projects.
|
|||
## JSON API
|
||||
|
||||
We also provide a JSON API that you can use like this:
|
||||
[/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ](https://alltubedownload.net/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ)
|
||||
`/json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ`
|
||||
|
||||
It returns a JSON object generated by youtube-dl.
|
||||
You can find a list of all the properties [in the youtube-dl documentation](https://github.com/ytdl-org/youtube-dl#output-template).
|
||||
|
|
|
@ -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",
|
||||
"description": "HTML GUI for youtube-dl",
|
||||
"repository": "https://github.com/Rudloff/alltube.git",
|
||||
"logo": "https://alltubedownload.net/img/logo.png",
|
||||
"keywords": [
|
||||
"alltube",
|
||||
"download",
|
||||
|
@ -28,6 +27,5 @@
|
|||
"value": "false",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"website": "https://alltubedownload.net/"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use Alltube\Controller\JsonController;
|
|||
use Alltube\Exception\ConfigException;
|
||||
use Alltube\Exception\DependencyException;
|
||||
use Alltube\Factory\ConfigFactory;
|
||||
use Alltube\Factory\DebugBarFactory;
|
||||
use Alltube\Factory\LocaleManagerFactory;
|
||||
use Alltube\Factory\LoggerFactory;
|
||||
use Alltube\Factory\SessionFactory;
|
||||
|
@ -16,6 +17,7 @@ use Alltube\Middleware\CspMiddleware;
|
|||
use Alltube\Middleware\LinkHeaderMiddleware;
|
||||
use Alltube\Middleware\LocaleMiddleware;
|
||||
use Alltube\Middleware\RouterPathMiddleware;
|
||||
use DebugBar\DebugBarException;
|
||||
use Slim\Container;
|
||||
use SmartyException;
|
||||
|
||||
|
@ -26,6 +28,7 @@ class App extends \Slim\App
|
|||
* @throws ConfigException
|
||||
* @throws DependencyException
|
||||
* @throws SmartyException
|
||||
* @throws DebugBarException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
@ -34,6 +37,8 @@ class App extends \Slim\App
|
|||
/** @var Container $container */
|
||||
$container = $this->getContainer();
|
||||
|
||||
$container['root_path'] = $this->getRootPath();
|
||||
|
||||
// Config.
|
||||
$container['config'] = ConfigFactory::create($container);
|
||||
|
||||
|
@ -43,16 +48,21 @@ class App extends \Slim\App
|
|||
// Locales.
|
||||
$container['locale'] = LocaleManagerFactory::create($container);
|
||||
|
||||
// Smarty.
|
||||
$container['view'] = ViewFactory::create($container);
|
||||
|
||||
// Logger.
|
||||
$container['logger'] = LoggerFactory::create($container);
|
||||
|
||||
if ($container->get('config')->debug) {
|
||||
// Debug bar.
|
||||
$container['debugbar'] = DebugBarFactory::create($container);
|
||||
}
|
||||
|
||||
// Smarty.
|
||||
$container['view'] = ViewFactory::create($container);
|
||||
|
||||
// Middlewares.
|
||||
$this->add(new LocaleMiddleware($container));
|
||||
$this->add(new CspMiddleware($container));
|
||||
$this->add(new LinkHeaderMiddleware($container));
|
||||
$this->add(new LinkHeaderMiddleware());
|
||||
$this->add(new RouterPathMiddleware($container));
|
||||
|
||||
// Controllers.
|
||||
|
@ -84,7 +94,7 @@ class App extends \Slim\App
|
|||
|
||||
$this->any(
|
||||
'/watch',
|
||||
[$frontController, 'info']
|
||||
[$frontController, 'watch']
|
||||
);
|
||||
|
||||
$this->any(
|
||||
|
@ -102,4 +112,17 @@ class App extends \Slim\App
|
|||
[$jsonController, '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
|
||||
{
|
||||
|
||||
/**
|
||||
* youtube-dl binary path.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $python = '/usr/bin/python';
|
||||
public string $python = '/usr/bin/python';
|
||||
|
||||
/**
|
||||
* youtube-dl parameters.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $convert = false;
|
||||
public bool $convert = false;
|
||||
|
||||
/**
|
||||
* Enable advanced conversion mode.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $convertAdvanced = false;
|
||||
public bool $convertAdvanced = false;
|
||||
|
||||
/**
|
||||
* List of formats available in advanced conversion mode.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $convertAdvancedFormats = ['mp3', 'avi', 'flv', 'wav'];
|
||||
public array $convertAdvancedFormats = ['mp3', 'avi', 'flv', 'wav'];
|
||||
|
||||
/**
|
||||
* ffmpeg binary path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ffmpeg = '/usr/bin/ffmpeg';
|
||||
public string $ffmpeg = '/usr/bin/ffmpeg';
|
||||
|
||||
/**
|
||||
* Path to the directory that contains the phantomjs binary.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $phantomjsDir = '/usr/bin/';
|
||||
public string $phantomjsDir = '/usr/bin/';
|
||||
|
||||
/**
|
||||
* Disable URL rewriting.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $uglyUrls = false;
|
||||
public bool $uglyUrls = false;
|
||||
|
||||
/**
|
||||
* Stream downloaded files trough server?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $stream = false;
|
||||
public bool $stream = false;
|
||||
|
||||
/**
|
||||
* Allow to remux video + audio?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $remux = false;
|
||||
public bool $remux = false;
|
||||
|
||||
/**
|
||||
* MP3 bitrate when converting (in kbit/s).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $audioBitrate = 128;
|
||||
public int $audioBitrate = 128;
|
||||
|
||||
/**
|
||||
* ffmpeg logging level.
|
||||
|
@ -110,21 +117,21 @@ class Config
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ffmpegVerbosity = 'error';
|
||||
public string $ffmpegVerbosity = 'error';
|
||||
|
||||
/**
|
||||
* App name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $appName = 'AllTube Download';
|
||||
public string $appName = 'AllTube Download';
|
||||
|
||||
/**
|
||||
* Generic formats supported by youtube-dl.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $genericFormats = [
|
||||
public array $genericFormats = [
|
||||
'best/bestvideo' => 'Best',
|
||||
'bestvideo+bestaudio' => 'Remux best video with best audio',
|
||||
'worst/worstvideo' => 'Worst',
|
||||
|
@ -135,26 +142,26 @@ class Config
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $debug = false;
|
||||
public bool $debug = false;
|
||||
|
||||
/**
|
||||
* Default to audio.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $defaultAudio = false;
|
||||
public bool $defaultAudio = false;
|
||||
|
||||
/**
|
||||
* Disable audio conversion from/to seeker.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $convertSeek = true;
|
||||
public bool $convertSeek = true;
|
||||
|
||||
/**
|
||||
* Config constructor.
|
||||
*
|
||||
* @param mixed[] $options Options
|
||||
* @param scalar[]|scalar[][]|null[] $options Options
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
|
@ -187,7 +194,7 @@ class Config
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function addHttpToFormat(string $format)
|
||||
public static function addHttpToFormat(string $format): string
|
||||
{
|
||||
$newFormat = [];
|
||||
foreach (explode('/', $format) as $subformat) {
|
||||
|
@ -205,7 +212,7 @@ class Config
|
|||
* @throws ConfigException If Python is missing
|
||||
* @throws ConfigException If youtube-dl is missing
|
||||
*/
|
||||
private function validateOptions()
|
||||
private function validateOptions(): void
|
||||
{
|
||||
if (!is_file($this->youtubedl)) {
|
||||
throw new ConfigException("Can't find youtube-dl at " . $this->youtubedl);
|
||||
|
@ -222,11 +229,11 @@ class Config
|
|||
/**
|
||||
* Apply the provided options.
|
||||
*
|
||||
* @param mixed[] $options Options
|
||||
* @param scalar[]|scalar[][]|null[] $options Options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function applyOptions(array $options)
|
||||
private function applyOptions(array $options): void
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
if (isset($this->$option) && isset($value)) {
|
||||
|
@ -243,7 +250,7 @@ class Config
|
|||
* @return void
|
||||
* @throws ConfigException
|
||||
*/
|
||||
private function getEnv()
|
||||
private function getEnv(): void
|
||||
{
|
||||
foreach (get_object_vars($this) as $prop => $value) {
|
||||
try {
|
||||
|
@ -266,7 +273,7 @@ class Config
|
|||
* @return Config
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public static function fromFile(string $file)
|
||||
public static function fromFile(string $file): Config
|
||||
{
|
||||
if (is_file($file)) {
|
||||
return new self(Yaml::parse(strval(file_get_contents($file))));
|
||||
|
@ -278,11 +285,11 @@ class Config
|
|||
/**
|
||||
* Manually set some options.
|
||||
*
|
||||
* @param mixed[] $options Options (see `config/config.example.yml` for available options)
|
||||
* @param scalar[]|scalar[][]|null[] $options Options (see `config/config.example.yml` for available options)
|
||||
* @return void
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
public function setOptions(array $options): void
|
||||
{
|
||||
$this->applyOptions($options);
|
||||
$this->validateOptions();
|
||||
|
@ -293,7 +300,7 @@ class Config
|
|||
*
|
||||
* @return Downloader
|
||||
*/
|
||||
public function getDownloader()
|
||||
public function getDownloader(): Downloader
|
||||
{
|
||||
return new Downloader(
|
||||
$this->youtubedl,
|
||||
|
@ -308,7 +315,7 @@ class Config
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAppVersion()
|
||||
public function getAppVersion(): string
|
||||
{
|
||||
$version = PrettyVersions::getRootPackageVersion();
|
||||
|
||||
|
|
|
@ -11,8 +11,11 @@ use Alltube\Library\Downloader;
|
|||
use Alltube\Library\Video;
|
||||
use Alltube\LocaleManager;
|
||||
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\Log\LoggerInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Router;
|
||||
|
@ -27,21 +30,21 @@ abstract class BaseController
|
|||
*
|
||||
* @var Video
|
||||
*/
|
||||
protected $video;
|
||||
protected Video $video;
|
||||
|
||||
/**
|
||||
* Default youtube-dl format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultFormat = 'best/bestvideo';
|
||||
protected string $defaultFormat = 'best/bestvideo';
|
||||
|
||||
/**
|
||||
* Slim dependency container.
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
protected ContainerInterface $container;
|
||||
|
||||
/**
|
||||
* Config instance.
|
||||
|
@ -72,7 +75,7 @@ abstract class BaseController
|
|||
protected $downloader;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
|
@ -111,7 +114,7 @@ abstract class BaseController
|
|||
*
|
||||
* @return string format
|
||||
*/
|
||||
protected function getFormat(Request $request)
|
||||
protected function getFormat(Request $request): string
|
||||
{
|
||||
$format = $request->getQueryParam('format');
|
||||
if (!isset($format)) {
|
||||
|
@ -126,11 +129,12 @@ abstract class BaseController
|
|||
*
|
||||
* @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');
|
||||
if (isset($password)) {
|
||||
|
@ -151,10 +155,23 @@ abstract class BaseController
|
|||
*
|
||||
* @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);
|
||||
|
||||
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\PlaylistArchiveStream;
|
||||
use Alltube\Stream\YoutubeStream;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Http\StatusCode;
|
||||
|
@ -37,56 +38,53 @@ class DownloadController extends BaseController
|
|||
*
|
||||
* @return Response HTTP response
|
||||
* @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 {
|
||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||
// Audio convert.
|
||||
return $this->getAudioResponse($request, $response);
|
||||
} elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
|
||||
// Advance convert.
|
||||
return $this->getConvertedResponse($request, $response);
|
||||
}
|
||||
try {
|
||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||
// Audio convert.
|
||||
return $this->getAudioResponse($request, $response);
|
||||
} elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
|
||||
// Advance convert.
|
||||
return $this->getConvertedResponse($request, $response);
|
||||
}
|
||||
|
||||
// Regular download.
|
||||
return $this->getDownloadResponse($request, $response);
|
||||
} catch (PasswordException $e) {
|
||||
$frontController = new FrontController($this->container);
|
||||
// Regular download.
|
||||
return $this->getDownloadResponse($request, $response);
|
||||
} catch (PasswordException $e) {
|
||||
$frontController = new FrontController($this->container);
|
||||
|
||||
return $frontController->password($request, $response);
|
||||
} catch (WrongPasswordException $e) {
|
||||
return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
|
||||
} catch (PlaylistConversionException $e) {
|
||||
return $frontController->password($request, $response);
|
||||
} catch (WrongPasswordException $e) {
|
||||
return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
|
||||
} 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(
|
||||
$request,
|
||||
$response,
|
||||
$this->localeManager->t('Conversion of playlists is not supported.')
|
||||
$this->localeManager->t('Conversion of M3U8 files is not supported.')
|
||||
);
|
||||
} catch (InvalidProtocolConversionException $e) {
|
||||
if (in_array($this->video->protocol, ['m3u8', 'm3u8_native'])) {
|
||||
return $this->displayError(
|
||||
$request,
|
||||
$response,
|
||||
$this->localeManager->t('Conversion of M3U8 files is not supported.')
|
||||
);
|
||||
} elseif ($this->video->protocol == 'http_dash_segments') {
|
||||
return $this->displayError(
|
||||
$request,
|
||||
$response,
|
||||
$this->localeManager->t('Conversion of DASH segments is not supported.')
|
||||
);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
} elseif ($this->video->protocol == 'http_dash_segments') {
|
||||
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
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getConvertedAudioResponse(Request $request, Response $response)
|
||||
private function getConvertedAudioResponse(Request $request, Response $response): Response
|
||||
{
|
||||
$from = null;
|
||||
$to = null;
|
||||
|
@ -135,7 +133,7 @@ class DownloadController extends BaseController
|
|||
* @throws PasswordException
|
||||
* @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'))) {
|
||||
// Force convert when we need to seek.
|
||||
|
@ -174,7 +172,7 @@ class DownloadController extends BaseController
|
|||
* @return Response HTTP response
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getStream(Request $request, Response $response)
|
||||
private function getStream(Request $request, Response $response): Response
|
||||
{
|
||||
if (isset($this->video->entries)) {
|
||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||
|
@ -222,13 +220,12 @@ class DownloadController extends BaseController
|
|||
if ($request->isGet()) {
|
||||
$response = $response->withBody($body);
|
||||
}
|
||||
$response = $response->withHeader(
|
||||
|
||||
return $response->withHeader(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="' .
|
||||
$this->video->getFilename() . '"'
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,7 +237,7 @@ class DownloadController extends BaseController
|
|||
* @return Response HTTP response
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getRemuxStream(Request $request, Response $response)
|
||||
private function getRemuxStream(Request $request, Response $response): Response
|
||||
{
|
||||
if (!$this->config->remux) {
|
||||
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
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getDownloadResponse(Request $request, Response $response)
|
||||
private function getDownloadResponse(Request $request, Response $response): Response
|
||||
{
|
||||
try {
|
||||
$videoUrls = $this->video->getUrl();
|
||||
|
@ -306,7 +303,7 @@ class DownloadController extends BaseController
|
|||
* @throws YoutubedlException
|
||||
* @throws PopenStreamException
|
||||
*/
|
||||
private function getConvertedResponse(Request $request, Response $response)
|
||||
private function getConvertedResponse(Request $request, Response $response): Response
|
||||
{
|
||||
$response = $response->withHeader(
|
||||
'Content-Disposition',
|
||||
|
|
|
@ -12,7 +12,10 @@ use Alltube\Library\Exception\WrongPasswordException;
|
|||
use Alltube\Locale;
|
||||
use Alltube\Middleware\CspMiddleware;
|
||||
use Exception;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\StatusCode;
|
||||
use Slim\Http\Uri;
|
||||
use stdClass;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Throwable;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
@ -52,7 +55,7 @@ class FrontController extends BaseController
|
|||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
public function index(Request $request, Response $response)
|
||||
public function index(Request $request, Response $response): Response
|
||||
{
|
||||
$this->view->render(
|
||||
$response,
|
||||
|
@ -60,7 +63,7 @@ class FrontController extends BaseController
|
|||
[
|
||||
'class' => 'index',
|
||||
'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(),
|
||||
]
|
||||
|
@ -78,7 +81,7 @@ class FrontController extends BaseController
|
|||
*
|
||||
* @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']));
|
||||
|
||||
|
@ -94,7 +97,7 @@ class FrontController extends BaseController
|
|||
* @return Response HTTP response
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
public function extractors(Request $request, Response $response)
|
||||
public function extractors(Request $request, Response $response): Response
|
||||
{
|
||||
$this->view->render(
|
||||
$response,
|
||||
|
@ -103,7 +106,7 @@ class FrontController extends BaseController
|
|||
'extractors' => $this->downloader->getExtractors(),
|
||||
'class' => 'extractors',
|
||||
'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'),
|
||||
]
|
||||
);
|
||||
|
@ -119,7 +122,7 @@ class FrontController extends BaseController
|
|||
*
|
||||
* @return Response HTTP response
|
||||
*/
|
||||
public function password(Request $request, Response $response)
|
||||
public function password(Request $request, Response $response): Response
|
||||
{
|
||||
$this->view->render(
|
||||
$response,
|
||||
|
@ -128,7 +131,7 @@ class FrontController extends BaseController
|
|||
'class' => 'password',
|
||||
'title' => $this->localeManager->t('Password prompt'),
|
||||
'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
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
private function getInfoResponse(Request $request, Response $response)
|
||||
private function getInfoResponse(Request $request, Response $response): Response
|
||||
{
|
||||
try {
|
||||
$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(
|
||||
$response,
|
||||
$template,
|
||||
|
@ -184,6 +231,7 @@ class FrontController extends BaseController
|
|||
'title' => $title,
|
||||
'description' => $description,
|
||||
'defaultFormat' => $this->defaultFormat,
|
||||
'formats' => $formats
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -198,24 +246,21 @@ class FrontController extends BaseController
|
|||
*
|
||||
* @return Response HTTP response
|
||||
* @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')) {
|
||||
// We skip the info page and get directly to the download.
|
||||
return $response->withRedirect(
|
||||
$this->router->pathFor('download', [], $request->getQueryParams())
|
||||
);
|
||||
} else {
|
||||
return $this->getInfoResponse($request, $response);
|
||||
}
|
||||
if ($this->config->convert && $request->getQueryParam('audio')) {
|
||||
// We skip the info page and get directly to the download.
|
||||
return $response->withRedirect(
|
||||
$this->router->pathFor('download', [], $request->getQueryParams())
|
||||
);
|
||||
} 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
|
||||
*/
|
||||
protected function displayError(Request $request, Response $response, string $message)
|
||||
protected function displayError(Request $request, Response $response, string $message): Response
|
||||
{
|
||||
$this->view->render(
|
||||
$response,
|
||||
|
@ -248,7 +293,7 @@ class FrontController extends BaseController
|
|||
* @param Response $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'))
|
||||
->withStatus(StatusCode::HTTP_NOT_FOUND);
|
||||
|
@ -259,7 +304,7 @@ class FrontController extends BaseController
|
|||
* @param Response $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'))
|
||||
->withStatus(StatusCode::HTTP_METHOD_NOT_ALLOWED);
|
||||
|
@ -274,7 +319,7 @@ class FrontController extends BaseController
|
|||
*
|
||||
* @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);
|
||||
|
||||
|
@ -285,7 +330,7 @@ class FrontController extends BaseController
|
|||
$response = $cspMiddleware->applyHeader($response);
|
||||
|
||||
if ($this->config->debug) {
|
||||
$renderer = new HtmlErrorRenderer(true);
|
||||
$renderer = new HtmlErrorRenderer(true, null, null, $this->container->get('root_path'));
|
||||
$exception = $renderer->render($error);
|
||||
|
||||
$response->getBody()->write($exception->getAsString());
|
||||
|
@ -304,4 +349,24 @@ class FrontController extends BaseController
|
|||
return $this->displayError($request, $response, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route that mimics YouTube video URLs ("/watch?v=foo")
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function watch(Request $request, Response $response): Response
|
||||
{
|
||||
// We build a full YouTube URL from the video ID.
|
||||
$youtubeUri = Uri::createFromString('https://www.youtube.com/watch')
|
||||
->withQuery(http_build_query(['v' => $request->getQueryParam('v')]));
|
||||
|
||||
// Then pass it to the info route.
|
||||
return $response->withRedirect(
|
||||
Uri::createFromString($this->router->pathFor('info'))
|
||||
->withQuery(http_build_query(['url' => strval($youtubeUri)]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Alltube\Controller;
|
||||
|
||||
use Alltube\Library\Exception\AlltubeLibraryException;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Http\StatusCode;
|
||||
|
@ -25,11 +26,11 @@ class JsonController extends BaseController
|
|||
* @return Response HTTP response
|
||||
* @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(
|
||||
$url,
|
||||
$this->getFormat($request),
|
||||
|
@ -37,8 +38,8 @@ class JsonController extends BaseController
|
|||
);
|
||||
|
||||
return $response->withJson($this->video->getJson());
|
||||
} else {
|
||||
return $response->withJson(['error' => 'You need to provide the url parameter'])
|
||||
} catch (InvalidURLException $e) {
|
||||
return $response->withJson(['error' => $e->getMessage()])
|
||||
->withStatus(StatusCode::HTTP_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Alltube;
|
||||
|
||||
use Slim\Http\StatusCode;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
|
@ -11,25 +12,24 @@ use Throwable;
|
|||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Last resort if the error has not been caught by the Slim error handler for some reason.
|
||||
* @param Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public static function handle(Throwable $e)
|
||||
public static function handle(Throwable $e): void
|
||||
{
|
||||
error_log($e);
|
||||
|
||||
if (class_exists(HtmlErrorRenderer::class)) {
|
||||
// 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);
|
||||
|
||||
http_response_code($exception->getStatusCode());
|
||||
die($exception->getAsString());
|
||||
} else {
|
||||
http_response_code(500);
|
||||
http_response_code(StatusCode::HTTP_INTERNAL_SERVER_ERROR);
|
||||
die('Error when starting the app: ' . htmlentities($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,4 @@ use Exception;
|
|||
*/
|
||||
class ConfigException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -10,5 +10,4 @@ use Exception;
|
|||
*/
|
||||
class DependencyException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -14,15 +14,14 @@ use Symfony\Component\ErrorHandler\Debug;
|
|||
*/
|
||||
class ConfigFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @return Config
|
||||
* @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)) {
|
||||
$config = Config::fromFile($configPath);
|
||||
} 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\LocaleManager;
|
||||
use Locale;
|
||||
use Slim\Container;
|
||||
|
||||
/**
|
||||
|
@ -12,15 +13,14 @@ use Slim\Container;
|
|||
*/
|
||||
class LocaleManagerFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @return LocaleManager|null
|
||||
* @return LocaleManager
|
||||
* @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.');
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
namespace Alltube\Factory;
|
||||
|
||||
use Consolidation\Log\Logger;
|
||||
use Consolidation\Log\LoggerManager;
|
||||
use Consolidation\Log\LogOutputStyler;
|
||||
use Slim\Container;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class LoggerFactory
|
||||
|
@ -13,23 +15,26 @@ use Symfony\Component\Console\Output\ConsoleOutput;
|
|||
*/
|
||||
class LoggerFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @return Logger
|
||||
* @return LoggerManager
|
||||
*/
|
||||
public static function create(Container $container)
|
||||
public static function create(Container $container): LoggerManager
|
||||
{
|
||||
$config = $container->get('config');
|
||||
if ($config->debug) {
|
||||
$verbosity = ConsoleOutput::VERBOSITY_DEBUG;
|
||||
$verbosity = OutputInterface::VERBOSITY_DEBUG;
|
||||
} else {
|
||||
$verbosity = ConsoleOutput::VERBOSITY_NORMAL;
|
||||
$verbosity = OutputInterface::VERBOSITY_NORMAL;
|
||||
}
|
||||
|
||||
$loggerManager = new LoggerManager();
|
||||
|
||||
$logger = new Logger(new ConsoleOutput($verbosity));
|
||||
$logger->setLogOutputStyler(new LogOutputStyler());
|
||||
|
||||
return $logger;
|
||||
$loggerManager->add('default', $logger);
|
||||
|
||||
return $loggerManager;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Alltube\Factory;
|
||||
|
||||
use Aura\Session\Session;
|
||||
use Aura\Session\SessionFactory as AuraSessionFactory;
|
||||
use Slim\Container;
|
||||
|
||||
/**
|
||||
|
@ -14,16 +15,15 @@ use Slim\Container;
|
|||
*/
|
||||
class SessionFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the current session.
|
||||
*
|
||||
* @param Container $container
|
||||
* @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->setCookieParams(['httponly' => true]);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Alltube\Factory;
|
||||
|
||||
use Alltube\LocaleManager;
|
||||
use Junker\DebugBar\Bridge\SmartyCollector;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Uri;
|
||||
|
@ -20,39 +21,41 @@ use SmartyException;
|
|||
class ViewFactory
|
||||
{
|
||||
/**
|
||||
* Generate the canonical URL of the current page.
|
||||
*
|
||||
* @param Request $request PSR-7 Request
|
||||
*
|
||||
* @return string URL
|
||||
* @param Uri $uri
|
||||
* @return Uri
|
||||
*/
|
||||
private static function getCanonicalUrl(Request $request)
|
||||
private static function cleanBasePath(Uri $uri): Uri
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = $request->getUri();
|
||||
$basePath = $uri->getBasePath();
|
||||
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')
|
||||
->withScheme('https');
|
||||
/*
|
||||
* When the base path ends with index.php,
|
||||
* 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|null $request PSR-7 request
|
||||
*
|
||||
* @return Smarty
|
||||
* @throws SmartyException
|
||||
* @param Request $request
|
||||
* @return Uri
|
||||
*/
|
||||
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 */
|
||||
$uri = $request->getUri();
|
||||
if (in_array('https', $request->getHeader('X-Forwarded-Proto'))) {
|
||||
|
@ -72,6 +75,28 @@ class ViewFactory
|
|||
$uri = $uri->withBasePath($path);
|
||||
}
|
||||
|
||||
return self::cleanBasePath($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Smarty view object.
|
||||
*
|
||||
* @param ContainerInterface $container Slim dependency container
|
||||
* @param Request|null $request PSR-7 request
|
||||
*
|
||||
* @return Smarty
|
||||
* @throws SmartyException
|
||||
*/
|
||||
public static function create(ContainerInterface $container, Request $request = null): Smarty
|
||||
{
|
||||
if (!isset($request)) {
|
||||
$request = $container->get('request');
|
||||
}
|
||||
|
||||
$view = new Smarty($container->get('root_path') . '/templates/');
|
||||
|
||||
$uri = self::prepareUri($request);
|
||||
|
||||
/** @var LocaleManager $localeManager */
|
||||
$localeManager = $container->get('locale');
|
||||
|
||||
|
@ -80,11 +105,24 @@ class ViewFactory
|
|||
$view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);
|
||||
$view->registerPlugin('block', 't', [$localeManager, 'smartyTranslate']);
|
||||
|
||||
$view->offsetSet('canonical', self::getCanonicalUrl($request));
|
||||
$view->offsetSet('locale', $container->get('locale')->getLocale());
|
||||
$view->offsetSet('locale', $container->get('locale'));
|
||||
$view->offsetSet('config', $container->get('config'));
|
||||
$view->offsetSet('domain', $uri->withBasePath('')->getBaseUrl());
|
||||
|
||||
if ($container->has('debugbar')) {
|
||||
$debugBar = $container->get('debugbar');
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class Locale
|
|||
*
|
||||
* @return string ISO 15897 code
|
||||
*/
|
||||
public function __toString()
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getIso15897();
|
||||
}
|
||||
|
@ -58,9 +58,9 @@ class Locale
|
|||
*
|
||||
* @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
|
||||
*/
|
||||
public function getIso15897()
|
||||
public function getIso15897(): string
|
||||
{
|
||||
if (isset($this->region)) {
|
||||
return $this->language . '_' . $this->region;
|
||||
|
@ -82,7 +82,7 @@ class Locale
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBcp47()
|
||||
public function getBcp47(): string
|
||||
{
|
||||
if (isset($this->region)) {
|
||||
return $this->language . '-' . $this->region;
|
||||
|
@ -96,7 +96,7 @@ class Locale
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIso3166()
|
||||
public function getIso3166(): string
|
||||
{
|
||||
return strtolower($this->region);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ use Symfony\Component\Translation\Loader\PoFileLoader;
|
|||
*/
|
||||
class LocaleManager
|
||||
{
|
||||
|
||||
/**
|
||||
* Path to locales.
|
||||
*/
|
||||
|
@ -28,14 +27,14 @@ class LocaleManager
|
|||
*
|
||||
* @var Locale|null
|
||||
*/
|
||||
private $curLocale;
|
||||
private ?Locale $curLocale = null;
|
||||
|
||||
/**
|
||||
* Session segment used to store session variables.
|
||||
*
|
||||
* @var Segment
|
||||
*/
|
||||
private $sessionSegment;
|
||||
private Segment $sessionSegment;
|
||||
|
||||
/**
|
||||
* Default locale.
|
||||
|
@ -49,7 +48,7 @@ class LocaleManager
|
|||
*
|
||||
* @var Translator
|
||||
*/
|
||||
private $translator;
|
||||
private Translator $translator;
|
||||
|
||||
/**
|
||||
* LocaleManager constructor.
|
||||
|
@ -80,7 +79,7 @@ class LocaleManager
|
|||
*
|
||||
* @return Locale[]
|
||||
*/
|
||||
public function getSupportedLocales()
|
||||
public function getSupportedLocales(): array
|
||||
{
|
||||
$return = [
|
||||
new Locale('en_US')
|
||||
|
@ -103,7 +102,7 @@ class LocaleManager
|
|||
*
|
||||
* @return Locale|null
|
||||
*/
|
||||
public function getLocale()
|
||||
public function getLocale(): ?Locale
|
||||
{
|
||||
return $this->curLocale;
|
||||
}
|
||||
|
@ -114,7 +113,7 @@ class LocaleManager
|
|||
* @param Locale $locale Locale
|
||||
* @return void
|
||||
*/
|
||||
public function setLocale(Locale $locale)
|
||||
public function setLocale(Locale $locale): void
|
||||
{
|
||||
$this->translator->setLocale($locale->getIso15897());
|
||||
$this->curLocale = $locale;
|
||||
|
@ -125,7 +124,7 @@ class LocaleManager
|
|||
* Unset the current locale.
|
||||
* @return void
|
||||
*/
|
||||
public function unsetLocale()
|
||||
public function unsetLocale(): void
|
||||
{
|
||||
$this->translator->setLocale(self::DEFAULT_LOCALE);
|
||||
$this->curLocale = null;
|
||||
|
@ -135,14 +134,14 @@ class LocaleManager
|
|||
/**
|
||||
* Smarty "t" block.
|
||||
*
|
||||
* @param mixed[] $params Block parameters
|
||||
* @param string[]|string[][] $params Block parameters
|
||||
* @param string|null $text Block content
|
||||
*
|
||||
* @return string Translated string
|
||||
*/
|
||||
public function smartyTranslate(array $params, string $text = null)
|
||||
public function smartyTranslate(array $params, string $text = null): string
|
||||
{
|
||||
if (isset($params['params'])) {
|
||||
if (isset($params['params']) && is_array($params['params'])) {
|
||||
return $this->t($text, $params['params']);
|
||||
} else {
|
||||
return $this->t($text);
|
||||
|
@ -154,10 +153,10 @@ class LocaleManager
|
|||
*
|
||||
* @param string|null $string $string String to translate
|
||||
*
|
||||
* @param mixed[] $params
|
||||
* @param string[] $params
|
||||
* @return string Translated string
|
||||
*/
|
||||
public function t(string $string = null, array $params = [])
|
||||
public function t(string $string = null, array $params = []): string
|
||||
{
|
||||
if (isset($string)) {
|
||||
return $this->translator->trans($string, $params);
|
||||
|
|
|
@ -15,7 +15,6 @@ use Slim\Http\Response;
|
|||
*/
|
||||
class CspMiddleware
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
|
@ -34,22 +33,24 @@ class CspMiddleware
|
|||
* @param Response $response
|
||||
* @return MessageInterface
|
||||
*/
|
||||
public function applyHeader(Response $response)
|
||||
public function applyHeader(Response $response): MessageInterface
|
||||
{
|
||||
$csp = new CSPBuilder();
|
||||
$csp->addDirective('default-src', [])
|
||||
$csp->disableOldBrowserSupport()
|
||||
->addDirective('default-src', [])
|
||||
->addDirective('font-src', ['self' => true])
|
||||
->addDirective('style-src', ['self' => true])
|
||||
->addDirective('form-action', ['self' => true])
|
||||
->addDirective('manifest-src', ['self' => true])
|
||||
->addDirective('base-uri', [])
|
||||
->addDirective('frame-ancestors', [])
|
||||
->addSource('form-action', '*')
|
||||
->addSource('img-src', '*');
|
||||
|
||||
if ($this->config->debug) {
|
||||
// So symfony/debug and symfony/error-handler can work.
|
||||
$csp->setDirective('script-src', ['unsafe-inline' => true])
|
||||
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true]);
|
||||
// So maximebf/debugbar, symfony/debug and symfony/error-handler can work.
|
||||
$csp->setDirective('script-src', ['self' => true, 'unsafe-inline' => true])
|
||||
->setDirective('style-src', ['self' => true, 'unsafe-inline' => true])
|
||||
->addSource('img-src', 'data:');
|
||||
}
|
||||
|
||||
return $csp->injectCSPHeader($response);
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
namespace Alltube\Middleware;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Alltube\Factory\ViewFactory;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Router;
|
||||
|
||||
/**
|
||||
* Class LinkHeaderMiddleware
|
||||
|
@ -13,20 +12,6 @@ use Slim\Router;
|
|||
*/
|
||||
class LinkHeaderMiddleware
|
||||
{
|
||||
/**
|
||||
* @var Router
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* LinkHeaderMiddleware constructor.
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->router = $container->get('router');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
|
@ -35,12 +20,19 @@ class LinkHeaderMiddleware
|
|||
*/
|
||||
public function __invoke(Request $request, Response $response, callable $next)
|
||||
{
|
||||
$uri = ViewFactory::prepareUri($request);
|
||||
|
||||
$response = $response->withHeader(
|
||||
'Link',
|
||||
'<' . $this->router->getBasePath() . '/css/style.css>; rel=preload; as=style'
|
||||
implode(
|
||||
'; ',
|
||||
[
|
||||
'<' . $uri->getBasePath() . '/css/style.css>',
|
||||
'rel=preload', 'as=style'
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,11 @@ class LocaleMiddleware
|
|||
/**
|
||||
* Test if a locale can be used for the current user.
|
||||
*
|
||||
* @param mixed[] $proposedLocale Locale array created by AcceptLanguage::parse()
|
||||
* @param string[] $proposedLocale Locale array created by AcceptLanguage::parse()
|
||||
*
|
||||
* @return Locale|null Locale if chosen, nothing otherwise
|
||||
*/
|
||||
public function testLocale(array $proposedLocale)
|
||||
public function testLocale(array $proposedLocale): ?Locale
|
||||
{
|
||||
foreach ($this->localeManager->getSupportedLocales() as $locale) {
|
||||
$parsedLocale = AcceptLanguage::parse($locale);
|
||||
|
@ -67,7 +67,7 @@ class LocaleMiddleware
|
|||
*
|
||||
* @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');
|
||||
$curLocale = $this->localeManager->getLocale();
|
||||
|
|
|
@ -14,7 +14,6 @@ use Robo\Tasks;
|
|||
*/
|
||||
class ReleaseCommand extends Tasks
|
||||
{
|
||||
|
||||
/**
|
||||
* Create release archive
|
||||
* @return void
|
||||
|
@ -27,12 +26,12 @@ class ReleaseCommand extends Tasks
|
|||
$gitTask = $this->taskExec('git');
|
||||
$result = $gitTask
|
||||
->arg('describe')
|
||||
->interactive(false)
|
||||
->run();
|
||||
$result->provideOutputdata();
|
||||
|
||||
$tmpDir = $this->_tmpDir();
|
||||
|
||||
$filename = 'alltube-' . trim((string)$result->getOutputData()) . '.zip';
|
||||
$filename = 'alltube-' . trim($result->getMessage()) . '.zip';
|
||||
|
||||
/** @var FilesystemStack $rmTask */
|
||||
$rmTask = $this->taskFilesystemStack();
|
||||
|
|
|
@ -23,7 +23,7 @@ class ConvertedPlaylistArchiveStream extends PlaylistArchiveStream
|
|||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
protected function startVideoStream(Video $video)
|
||||
protected function startVideoStream(Video $video): void
|
||||
{
|
||||
$this->curVideoStream = new Stream($this->downloader->getAudioStream($video));
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @var Video[]
|
||||
*/
|
||||
private $videos = [];
|
||||
private array $videos = [];
|
||||
|
||||
/**
|
||||
* Stream used to store data before it is sent to the browser.
|
||||
|
@ -38,21 +38,21 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @var StreamInterface
|
||||
*/
|
||||
protected $curVideoStream;
|
||||
protected StreamInterface $curVideoStream;
|
||||
|
||||
/**
|
||||
* True if the archive is complete.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $isComplete = false;
|
||||
private bool $isComplete = false;
|
||||
|
||||
/**
|
||||
* Downloader object.
|
||||
*
|
||||
* @var Downloader
|
||||
*/
|
||||
protected $downloader;
|
||||
protected Downloader $downloader;
|
||||
|
||||
/**
|
||||
* PlaylistArchiveStream constructor.
|
||||
|
@ -83,7 +83,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function send($data)
|
||||
protected function send($data): void
|
||||
{
|
||||
$pos = $this->tell();
|
||||
|
||||
|
@ -113,7 +113,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSize()
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSeekable()
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
rewind($this->buffer);
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWritable()
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isReadable()
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @param string|null $key string $key Specific metadata to retrieve.
|
||||
*
|
||||
* @return array|mixed|null
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
|
@ -208,7 +208,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
public function __toString(): string
|
||||
{
|
||||
$this->rewind();
|
||||
|
||||
|
@ -233,7 +233,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
fseek($this->buffer, $offset, $whence);
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function eof()
|
||||
public function eof(): bool
|
||||
{
|
||||
return $this->isComplete && feof($this->buffer);
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
protected function startVideoStream(Video $video)
|
||||
protected function startVideoStream(Video $video): void
|
||||
{
|
||||
$response = $this->downloader->getHttpResponse($video);
|
||||
|
||||
|
@ -272,12 +272,12 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
/**
|
||||
* Read data from the stream.
|
||||
*
|
||||
* @param mixed $count Number of bytes to read
|
||||
* @param mixed $length Number of bytes to read
|
||||
*
|
||||
* @return string|false
|
||||
* @throws AlltubeLibraryException
|
||||
*/
|
||||
public function read($count)
|
||||
public function read($length)
|
||||
{
|
||||
// If the archive is complete, we only read the remaining buffer.
|
||||
if (!$this->isComplete) {
|
||||
|
@ -297,15 +297,22 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface
|
|||
}
|
||||
} else {
|
||||
// Continue streaming the current video.
|
||||
$this->stream_file_part($this->curVideoStream->read($count));
|
||||
$this->stream_file_part($this->curVideoStream->read($length));
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
*/
|
||||
public function close()
|
||||
public function close(): void
|
||||
{
|
||||
if (is_resource($this->buffer)) {
|
||||
fclose($this->buffer);
|
||||
|
|
|
@ -20,7 +20,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
private $response;
|
||||
private ResponseInterface $response;
|
||||
|
||||
/**
|
||||
* YoutubeChunkStream constructor.
|
||||
|
@ -39,7 +39,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function read($length)
|
||||
public function read($length): string
|
||||
{
|
||||
$size = intval($this->response->getHeader('Content-Length')[0]);
|
||||
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.
|
||||
*/
|
||||
public function __toString()
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->response->getBody();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
public function close(): void
|
||||
{
|
||||
$this->response->getBody()->close();
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSize()
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->response->getBody()->getSize();
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return int
|
||||
*/
|
||||
public function tell()
|
||||
public function tell(): int
|
||||
{
|
||||
return $this->response->getBody()->tell();
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function eof()
|
||||
public function eof(): bool
|
||||
{
|
||||
return $this->response->getBody()->eof();
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSeekable()
|
||||
public function isSeekable(): bool
|
||||
{
|
||||
return $this->response->getBody()->isSeekable();
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
$this->response->getBody()->seek($offset, $whence);
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->response->getBody()->rewind();
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWritable()
|
||||
public function isWritable(): bool
|
||||
{
|
||||
return $this->response->getBody()->isWritable();
|
||||
}
|
||||
|
@ -156,9 +156,9 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @param mixed $string The string that is to be written
|
||||
*
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function write($string)
|
||||
public function write($string): int
|
||||
{
|
||||
return $this->response->getBody()->write($string);
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isReadable()
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return $this->response->getBody()->isReadable();
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContents()
|
||||
public function getContents(): string
|
||||
{
|
||||
return $this->response->getBody()->getContents();
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ class YoutubeChunkStream implements StreamInterface
|
|||
*
|
||||
* @param string|null $key Specific metadata to retrieve.
|
||||
*
|
||||
* @return array|mixed|null
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
|
|
|
@ -22,11 +22,11 @@ class UglyRouter extends Router
|
|||
*
|
||||
* @param ServerRequestInterface $request The current HTTP request object
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return int[]|string[]|array[]
|
||||
*
|
||||
* @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
|
||||
*/
|
||||
public function dispatch(ServerRequestInterface $request)
|
||||
public function dispatch(ServerRequestInterface $request): array
|
||||
{
|
||||
$params = $request->getQueryParams();
|
||||
$uri = new Uri('', '');
|
||||
|
@ -53,9 +53,9 @@ class UglyRouter extends Router
|
|||
* @throws InvalidArgumentException If required data not provided
|
||||
* @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('');
|
||||
|
||||
if ($this->basePath) {
|
||||
|
|
100
composer.json
100
composer.json
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"name": "rudloff/alltube",
|
||||
"type": "project",
|
||||
"description": "HTML GUI for youtube-dl",
|
||||
"homepage": "http://alltubedownload.net/",
|
||||
"license": "GPL-3.0-only",
|
||||
"type": "project",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Pierre Rudloff",
|
||||
|
@ -14,47 +13,86 @@
|
|||
{
|
||||
"name": "Olivier Haquette",
|
||||
"email": "contact@olivierhaquette.fr",
|
||||
"homepage": "http://olivierhaquette.fr/",
|
||||
"homepage": "https://ographik.fr/",
|
||||
"role": "Designer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"aura/session": "^2.1",
|
||||
"barracudanetworks/archivestream-php": "^1.0",
|
||||
"consolidation/log": "^2.0",
|
||||
"cweagans/composer-patches": "^1.7",
|
||||
"j0k3r/httplug-ssrf-plugin": "^2.0",
|
||||
"jawira/case-converter": "^3.4",
|
||||
"jean85/pretty-package-versions": "^1.3",
|
||||
"mathmarques/smarty-view": "^1.1",
|
||||
"mathmarques/smarty-view": "^1.2",
|
||||
"oomphinc/composer-installers-extender": "^2.0",
|
||||
"paragonie/csp-builder": "^2.5",
|
||||
"rinvex/countries": "^6.1",
|
||||
"rudloff/alltube-library": "^0.1.1",
|
||||
"symfony/finder": "^5.0",
|
||||
"rinvex/countries": "^7.3",
|
||||
"rudloff/alltube-library": "^0.1.3",
|
||||
"symfony/finder": "^5.4",
|
||||
"symfony/translation": "^4.0",
|
||||
"symfony/yaml": "^4.0",
|
||||
"webfontkit/open-sans": "^1.0",
|
||||
"ytdl-org/youtube-dl": "^2020.09",
|
||||
"yt-dlp/yt-dlp": "^2023.03",
|
||||
"zonuexe/http-accept-language": "^0.4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"consolidation/robo": "^2.1",
|
||||
"ergebnis/composer-normalize": "^2.6",
|
||||
"insite/composer-dangling-locked-deps": "^0.2.0",
|
||||
"consolidation/robo": "^3.0",
|
||||
"enlightn/security-checker": "^1.4",
|
||||
"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",
|
||||
"phpro/grumphp": "^1.0",
|
||||
"phpstan/phpstan": "^0.12.25",
|
||||
"phpunit/phpunit": "^8.4",
|
||||
"sensiolabs/security-checker": "^6.0",
|
||||
"phpro/grumphp": "^1.3",
|
||||
"phpstan/phpstan": "^0.12.72",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"povils/phpmnd": "^2.5",
|
||||
"smarty-gettext/smarty-gettext": "^1.6",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/error-handler": "^5.0",
|
||||
"symfony/var-dumper": "^5.0"
|
||||
"symfony/error-handler": "^5.4",
|
||||
"symfony/var-dumper": "^5.4"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "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": {
|
||||
"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": {
|
||||
"php": "7.3.11"
|
||||
"php": "7.4.33"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
|
@ -69,31 +107,9 @@
|
|||
},
|
||||
"installer-types": [
|
||||
"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.09.20",
|
||||
"dist": {
|
||||
"type": "tar",
|
||||
"url": "https://files.pythonhosted.org/packages/12/8b/51cae2929739d637fdfbc706b2d5f8925b5710d8f408b5319a07ea45fe99/youtube_dl-2020.9.20.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "grumphp run --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
|
||||
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
|
||||
python: /usr/bin/python
|
||||
|
@ -12,6 +12,8 @@ params:
|
|||
- --flat-playlist
|
||||
- --restrict-filenames
|
||||
- --no-playlist
|
||||
- --use-extractors
|
||||
- default,-generic
|
||||
|
||||
# True to enable audio conversion
|
||||
convert: false
|
||||
|
|
|
@ -255,7 +255,7 @@ footer a:hover {
|
|||
margin-top: 12px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 622px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mp3-inner {
|
||||
|
@ -545,6 +545,7 @@ h1 {
|
|||
|
||||
.thumb {
|
||||
max-width: 700px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.format {
|
||||
|
|
|
@ -10,9 +10,10 @@ grumphp:
|
|||
xmllint: ~
|
||||
yamllint: ~
|
||||
composer: ~
|
||||
securitychecker: ~
|
||||
securitychecker_enlightn: ~
|
||||
composer_normalize: ~
|
||||
composer_dangling_locked_deps: ~
|
||||
phpmnd: ~
|
||||
phpcs:
|
||||
standard: PSR12
|
||||
phpstan:
|
||||
|
|
|
@ -98,7 +98,7 @@ msgid "Share on Facebook"
|
|||
msgstr "شاركها على فيسبوك"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "انسخ هنا رابط الفيديو (يوتيوب، انستقرام، وغيرها)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
|
|
|
@ -131,8 +131,8 @@ msgid "Video password"
|
|||
msgstr "Videopasswort"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Kopiere hier die URL deines Videos (Youtube, Dailymotion, etc.) hinein"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Kopiere hier die URL deines Videos (YouTube, Dailymotion, etc.) hinein"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
|||
msgstr "Compartir en Facebook"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Copia aquí la URL de tu vídeo (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Copia aquí la URL de tu vídeo (YouTube, Dailymotion, etc.)"
|
||||
|
||||
#: templates/index.tpl:23
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -69,8 +69,8 @@ msgid "Video password"
|
|||
msgstr "Mot de passe de la vidéo"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Copiez ici l'URL de votre vidéo (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.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
@ -219,17 +219,17 @@ msgstr "Impossible de trouver l'URL de la vidéo."
|
|||
|
||||
#: controllers/FrontController.php:64
|
||||
msgid ""
|
||||
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
|
||||
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||
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."
|
||||
|
||||
#: controllers/FrontController.php:110
|
||||
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"
|
||||
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"
|
||||
|
||||
#: controllers/FrontController.php:136
|
||||
|
@ -238,9 +238,9 @@ msgstr "Demande de mot de passe"
|
|||
|
||||
#: controllers/FrontController.php:138
|
||||
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 ""
|
||||
"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"
|
||||
|
||||
#: controllers/FrontController.php:169
|
||||
|
|
|
@ -59,8 +59,8 @@ msgid "Video password"
|
|||
msgstr "Password del video"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Copia qui l'URL del video (Youtube, Dailymotion, ecc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Copia qui l'URL del video (YouTube, Dailymotion, ecc.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -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"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Zamieść link do wideo (Yotube, Dailymotion, itp.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Zamieść link do wideo (YouTube, Dailymotion, itp.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -106,8 +106,8 @@ msgid "Share on Facebook"
|
|||
msgstr "Compartilhe no Facebook"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Cole aqui a URL do vídeo (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Cole aqui a URL do vídeo (YouTube, Dailymotion, etc.)"
|
||||
|
||||
#: templates/index.tpl:24
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr ""
|
||||
|
||||
#: templates/index.tpl:25
|
||||
|
@ -173,12 +173,12 @@ msgstr ""
|
|||
|
||||
#: classes/Controller/FrontController.php:65
|
||||
msgid ""
|
||||
"Easily download videos from Youtube, Dailymotion, Vimeo and other websites."
|
||||
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||
msgstr ""
|
||||
|
||||
#: classes/Controller/FrontController.php:112
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
|
@ -188,7 +188,7 @@ msgstr ""
|
|||
|
||||
#: classes/Controller/FrontController.php:140
|
||||
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 ""
|
||||
|
||||
#: classes/Controller/FrontController.php:174
|
||||
|
|
|
@ -65,8 +65,8 @@ msgid "Video password"
|
|||
msgstr "Video parolası"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "Videonuzun URL'sini buraya kopyalayın (Youtube, Dailymotion, vb.)"
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "Videonuzun URL'sini buraya kopyalayın (YouTube, Dailymotion, vb.)"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
|
|
|
@ -1,134 +1,227 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: POEditor.com\n"
|
||||
"Project-Id-Version: AllTube Download\n"
|
||||
"Language: zh-CN\n"
|
||||
|
||||
#: templates/error.tpl:6
|
||||
msgid "Please check the URL of your video."
|
||||
msgstr "请检查您的视频的 URL。"
|
||||
|
||||
#: templates/playlist.tpl:5
|
||||
msgid "Videos extracted from"
|
||||
msgstr "视频提取自"
|
||||
|
||||
#: templates/playlist.tpl:7
|
||||
msgid ":"
|
||||
msgstr ":"
|
||||
|
||||
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83
|
||||
#: templates/video.tpl:86 templates/index.tpl:18
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: templates/playlist.tpl:27
|
||||
msgid "More options"
|
||||
msgstr "更多选项"
|
||||
|
||||
#: templates/password.tpl:5
|
||||
msgid "This video is protected"
|
||||
msgstr "这个视频受保护"
|
||||
|
||||
#: templates/password.tpl:6
|
||||
msgid "You need a password in order to download this video."
|
||||
msgstr "你需要密码才能下载这个视频。"
|
||||
|
||||
#: templates/password.tpl:8
|
||||
msgid "Video password"
|
||||
msgstr "视频密码"
|
||||
|
||||
#: templates/extractors.tpl:4
|
||||
msgid "Supported websites"
|
||||
msgstr "支持的网站"
|
||||
|
||||
#: templates/video.tpl:6
|
||||
msgid "You are going to download"
|
||||
msgstr "你即将下载"
|
||||
|
||||
#: templates/video.tpl:24
|
||||
msgid "Available formats:"
|
||||
msgstr "可用的格式︰"
|
||||
|
||||
#: templates/video.tpl:29
|
||||
msgid "Generic formats"
|
||||
msgstr "通用格式"
|
||||
|
||||
#: templates/video.tpl:32
|
||||
msgid "Best"
|
||||
msgstr "最佳"
|
||||
|
||||
#: templates/video.tpl:37
|
||||
msgid "Remux best video with best audio"
|
||||
msgstr "重新封装最佳视频与最佳音频"
|
||||
|
||||
#: templates/video.tpl:41
|
||||
msgid "Worst"
|
||||
msgstr "最差"
|
||||
|
||||
#: templates/video.tpl:44
|
||||
msgid "Detailed formats"
|
||||
msgstr "详细格式"
|
||||
|
||||
#: templates/inc/footer.tpl:4
|
||||
msgid "Code by"
|
||||
msgstr "代码来自"
|
||||
|
||||
#: templates/inc/footer.tpl:6
|
||||
msgid "Design by"
|
||||
msgstr "设计来自"
|
||||
|
||||
#: templates/inc/footer.tpl:12
|
||||
msgid "AllTube Download on Facebook"
|
||||
msgstr "去Alltube Download的Facebook页面"
|
||||
|
||||
#: templates/inc/footer.tpl:12
|
||||
msgid "Like us on Facebook"
|
||||
msgstr "在Facebook关注我们"
|
||||
|
||||
#: templates/inc/footer.tpl:14
|
||||
msgid "Get the code"
|
||||
msgstr "获取代码"
|
||||
#: templates/inc/footer.tpl:8
|
||||
msgid "Code by @dev"
|
||||
msgstr "由 @dev 开发"
|
||||
|
||||
#: templates/inc/footer.tpl:16
|
||||
msgid "Based on"
|
||||
msgstr "基于"
|
||||
msgid "Design by @designer"
|
||||
msgstr "由 @designer 设计"
|
||||
|
||||
#: templates/inc/header.tpl:21
|
||||
msgid "Share on Twitter"
|
||||
msgstr "分享到 Twitter"
|
||||
#: templates/inc/footer.tpl:21
|
||||
msgid "Get the code"
|
||||
msgstr "获取源代码"
|
||||
|
||||
#: templates/inc/header.tpl:23
|
||||
msgid "Share on Facebook"
|
||||
msgstr "分享到 Facebook"
|
||||
#: templates/inc/footer.tpl:29
|
||||
msgid "Based on @youtubedl"
|
||||
msgstr "基于 @youtubedl"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
msgid "Copy here the URL of your video (Youtube, Dailymotion, etc.)"
|
||||
msgstr "在这里复制您的视频 (Youtube、 Dailymotion 等) 的 URL"
|
||||
#: templates/inc/footer.tpl:33
|
||||
msgid "Donate using Liberapay"
|
||||
msgstr "使用 Liberapay 捐赠"
|
||||
|
||||
#: templates/index.tpl:23
|
||||
msgid "Audio only (MP3)"
|
||||
msgstr "仅限音频(mp3)"
|
||||
|
||||
#: templates/index.tpl:28
|
||||
msgid "See all supported websites"
|
||||
msgstr "请参阅支持的所有网站"
|
||||
|
||||
#: templates/index.tpl:30
|
||||
msgid "Drag this to your bookmarks bar:"
|
||||
msgstr "把这个拖到你的书签:"
|
||||
|
||||
#: templates/index.tpl:31
|
||||
msgid "Bookmarklet"
|
||||
msgstr "书签工具"
|
||||
#: templates/inc/footer.tpl:35
|
||||
msgid "Donate"
|
||||
msgstr "捐赠"
|
||||
|
||||
#: templates/inc/header.tpl:4
|
||||
msgid "Switch language"
|
||||
msgstr "切换语言"
|
||||
|
||||
#: templates/inc/header.tpl:8
|
||||
msgid "Set language"
|
||||
msgstr "设置语言"
|
||||
|
||||
#: templates/info.tpl:11
|
||||
msgid "You are going to download @title."
|
||||
msgstr "您将要下载 @title。"
|
||||
|
||||
#: templates/info.tpl:29
|
||||
msgid "Available formats:"
|
||||
msgstr "可用的格式:"
|
||||
|
||||
#: templates/info.tpl:31
|
||||
msgid "Generic formats"
|
||||
msgstr "通用格式"
|
||||
|
||||
#: templates/info.tpl:35
|
||||
msgid "Best"
|
||||
msgstr "最佳"
|
||||
|
||||
#: templates/info.tpl:36
|
||||
msgid "Remux best video with best audio"
|
||||
msgstr "重新封装最佳视频和最佳音频"
|
||||
|
||||
#: templates/info.tpl:37
|
||||
msgid "Worst"
|
||||
msgstr "最差"
|
||||
|
||||
#: templates/info.tpl:42
|
||||
msgid "Detailed formats"
|
||||
msgstr "详细格式"
|
||||
|
||||
#: templates/info.tpl:86
|
||||
msgid "Stream the video through the server"
|
||||
msgstr "通过服务器传输视频"
|
||||
|
||||
#: templates/info.tpl:92
|
||||
msgid "Convert into a custom format:"
|
||||
msgstr "转换为自定义格式:"
|
||||
|
||||
#: templates/info.tpl:93
|
||||
msgid "Custom format"
|
||||
msgstr "自定义格式"
|
||||
|
||||
#: templates/info.tpl:93
|
||||
msgid "Format to convert to"
|
||||
msgstr "要转换到的格式"
|
||||
|
||||
#: templates/info.tpl:98
|
||||
# Other translators: Please check that file for context
|
||||
msgid "with"
|
||||
msgstr ",并带"
|
||||
|
||||
#: templates/info.tpl:99
|
||||
msgid "Bit rate"
|
||||
msgstr "比特率"
|
||||
|
||||
#: templates/info.tpl:100
|
||||
msgid "Custom bitrate"
|
||||
msgstr "自定义比特率"
|
||||
|
||||
#: templates/info.tpl:103
|
||||
msgid "kbit/s audio"
|
||||
msgstr "kbit/s 的音频"
|
||||
|
||||
#: templates/info.tpl:107 templates/playlist.tpl:38 templates/password.tpl:11
|
||||
#: templates/index.tpl:19
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: templates/playlist.tpl:12
|
||||
msgid "Videos extracted from @title:"
|
||||
msgstr "从 @title 中提取的视频:"
|
||||
|
||||
#: templates/playlist.tpl:39
|
||||
msgid "More options"
|
||||
msgstr "更多选项"
|
||||
|
||||
#: templates/extractors.tpl:4 classes/Controller/FrontController.php:111
|
||||
msgid "Supported websites"
|
||||
msgstr "支持的网站"
|
||||
|
||||
#: templates/error.tpl:5
|
||||
msgid "An error occurred"
|
||||
msgstr "出错了"
|
||||
|
||||
#: templates/password.tpl:5
|
||||
msgid "This video is protected"
|
||||
msgstr "此视频受保护"
|
||||
|
||||
#: templates/password.tpl:6
|
||||
msgid "You need a password in order to download this video."
|
||||
msgstr "您需要密码才能下载此视频。"
|
||||
|
||||
#: templates/password.tpl:8
|
||||
msgid "Video password"
|
||||
msgstr "视频密码"
|
||||
|
||||
#: templates/index.tpl:8
|
||||
# I don't think this needs to be a 100% match
|
||||
msgid "Copy here the URL of your video (YouTube, Dailymotion, etc.)"
|
||||
msgstr "在此处粘贴视频网址"
|
||||
|
||||
#: templates/index.tpl:25
|
||||
msgid "Audio only (MP3)"
|
||||
msgstr "仅音频(MP3)"
|
||||
|
||||
#: templates/index.tpl:29
|
||||
# Still check that file for context
|
||||
msgid "From"
|
||||
msgstr "从"
|
||||
|
||||
#: templates/index.tpl:32
|
||||
msgid "to"
|
||||
msgstr "到"
|
||||
|
||||
#: templates/index.tpl:41
|
||||
msgid "See all supported websites"
|
||||
msgstr "查看所有支持的网站"
|
||||
|
||||
#: templates/index.tpl:43
|
||||
msgid "Drag this to your bookmarks bar:"
|
||||
msgstr "您可以把这个书签工具拖到您的书签栏中:"
|
||||
|
||||
#: templates/index.tpl:45
|
||||
msgid "Bookmarklet"
|
||||
msgstr "书签工具"
|
||||
|
||||
#: classes/Controller/DownloadController.php:64
|
||||
#: classes/Controller/FrontController.php:166
|
||||
msgid "Wrong password"
|
||||
msgstr "密码错误"
|
||||
|
||||
#: classes/Controller/DownloadController.php:69
|
||||
msgid "Conversion of playlists is not supported."
|
||||
msgstr "不支持转换播放列表。"
|
||||
|
||||
#: classes/Controller/DownloadController.php:76
|
||||
msgid "Conversion of M3U8 files is not supported."
|
||||
msgstr "不支持转换 M3U8 文件。"
|
||||
|
||||
#: classes/Controller/DownloadController.php:82
|
||||
# ref. Chinese Wikipedia article about DASH
|
||||
msgid "Conversion of DASH segments is not supported."
|
||||
msgstr "不支持转换 DASH 片段。"
|
||||
|
||||
#: classes/Controller/FrontController.php:65
|
||||
msgid ""
|
||||
"Easily download videos from YouTube, Dailymotion, Vimeo and other websites."
|
||||
msgstr ""
|
||||
"轻松从 YouTube、Dailymotion、Vimeo 等网站下载视频。"
|
||||
|
||||
#: classes/Controller/FrontController.php:112
|
||||
# NOTE: DON'T translate AllTube Download
|
||||
msgid ""
|
||||
"List of all supported websites from which AllTube Download can extract video "
|
||||
"or audio files"
|
||||
msgstr ""
|
||||
"AllTube Download 能够提取视频"
|
||||
"或音频文件的的所有网站"
|
||||
|
||||
#: classes/Controller/FrontController.php:138
|
||||
msgid "Password prompt"
|
||||
msgstr "密码提示"
|
||||
|
||||
#: classes/Controller/FrontController.php:140
|
||||
msgid ""
|
||||
"You need a password in order to download this video with AllTube Download"
|
||||
msgstr ""
|
||||
"您需要密码才能使用 AllTube Download 下载此视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:174
|
||||
# Download page header?
|
||||
msgid "Video download"
|
||||
msgstr "下载视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:176
|
||||
msgid "Download video from @extractor"
|
||||
msgstr "从 @extractor 下载视频"
|
||||
|
||||
#: classes/Controller/FrontController.php:182
|
||||
msgid "Download @title from @extractor"
|
||||
msgstr "从 @extractor 下载 @title"
|
||||
|
||||
#: classes/Controller/FrontController.php:255
|
||||
msgid "Error"
|
||||
msgstr "错误"
|
||||
|
||||
#: classes/Controller/FrontController.php:271
|
||||
msgid "Page not found"
|
||||
msgstr "找不到页面"
|
||||
|
||||
#: classes/Controller/FrontController.php:282
|
||||
msgid "Method not allowed"
|
||||
msgstr "不允许此请求方法"
|
||||
|
|
|
@ -5,11 +5,6 @@ require_once __DIR__ . '/vendor/autoload.php';
|
|||
use Alltube\App;
|
||||
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 {
|
||||
// Create app.
|
||||
$app = new App();
|
||||
|
|
15
phpunit.xml
15
phpunit.xml
|
@ -1,17 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<phpunit bootstrap="tests/bootstrap.php">
|
||||
<filter>
|
||||
<whitelist>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>classes/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log type="coverage-html" target="coverage/"/>
|
||||
<log type="coverage-clover" target="clover.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
|
|
@ -6,20 +6,6 @@ Most recent browsers automatically play a video
|
|||
if it is a format they know how to play.
|
||||
You can usually download the video by doing *File > Save to* or *ctrl + S*.
|
||||
|
||||
## [alltubedownload.net](https://alltubedownload.net) is too slow
|
||||
|
||||
[alltubedownload.net](https://alltubedownload.net) is hosted on a free [Heroku server](https://www.heroku.com/pricing)
|
||||
so it has low RAM and CPU.
|
||||
|
||||
AllTube probably won't switch to a more expensive hosting
|
||||
because this project does not earn any financial 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
|
||||
|
||||
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 enable streaming videos through the server (see below).
|
||||
Please note that this can use a lot of resources on the server
|
||||
(which is why we won't enable it on alltubedownload.net).
|
||||
Please note that this can use a lot of resources on the server.
|
||||
|
||||
## I get a 404 error on every page except the index
|
||||
|
||||
|
@ -91,7 +76,7 @@ You need to add this to your `config.yml` file:
|
|||
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
|
||||
|
||||
|
@ -129,7 +114,7 @@ remux: true
|
|||
## Convert videos to something other than 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:
|
||||
|
||||
```yaml
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
}
|
||||
],
|
||||
"lang": "en",
|
||||
"start_url": "./",
|
||||
"start_url": "../",
|
||||
"theme_color": "#4F4F4F",
|
||||
"background_color": "#EBEBEB",
|
||||
"orientation": "portrait"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://alltubedownload.net/</loc>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>1</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://alltubedownload.net/extractors</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>
|
||||
</urlset>
|
|
@ -1 +0,0 @@
|
|||
Sitemap: https://alltubedownload.net/resources/sitemap.xml
|
|
@ -1 +1 @@
|
|||
python-3.8.6
|
||||
python-3.8.12
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{include file='inc/head.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main error">
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div class="error">
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}An error occurred{/t}</h2>
|
||||
<p><i>{$error|escape|nl2br}</i></p>
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{include file='inc/head.tpl'}
|
||||
{include file='inc/header.tpl'}
|
||||
{include file='inc/logo.tpl'}
|
||||
<h2 class="titre">{t}Supported websites{/t}</h2>
|
||||
<div class="tripleliste">
|
||||
<ul>
|
||||
{foreach $extractors as $extractor}
|
||||
<li>{$extractor}</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
{include file='inc/footer.tpl'}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file='inc/logo.tpl'}
|
||||
<h2 class="titre">{t}Supported websites{/t}</h2>
|
||||
<div class="tripleliste">
|
||||
<ul>
|
||||
{foreach $extractors as $extractor}
|
||||
<li>{$extractor}</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
</div>
|
||||
<footer class="small-font">
|
||||
<div class="footer_wrapper">
|
||||
{$dev="<a rel='author' target='blank'
|
||||
href='http://rudloff.pro/'>
|
||||
Pierre Rudloff
|
||||
</a>"}
|
||||
{include file='snippets/dev.tpl' assign=dev}
|
||||
{t params=['@dev'=>$dev]}Code by @dev{/t}
|
||||
|
||||
·
|
||||
|
||||
{$designer="<a rel='author' target='blank'
|
||||
href='http://olivierhaquette.fr'>
|
||||
Olivier Haquette
|
||||
</a>"}
|
||||
{include file='snippets/designer.tpl' assign=designer}
|
||||
{t params=['@designer' => $designer]}Design by @designer{/t}
|
||||
|
||||
·
|
||||
|
@ -23,19 +16,7 @@
|
|||
|
||||
·
|
||||
|
||||
{$youtubedl="<a href='http://ytdl-org.github.io/youtube-dl/'>
|
||||
youtube-dl
|
||||
</a>"}
|
||||
{include file='snippets/youtubedl.tpl' assign=youtubedl}
|
||||
{t params=['@youtubedl'=>$youtubedl]}Based on @youtubedl{/t}
|
||||
|
||||
·
|
||||
|
||||
<a rel="noopener" target="_blank" title="{t}Donate using Liberapay{/t}"
|
||||
href="https://liberapay.com/Rudloff/donate">
|
||||
{t}Donate{/t}
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
<!doctype html>
|
||||
<html {if isset($locale)}lang="{$locale->getBcp47()}"{/if}>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name=viewport content="width=device-width, initial-scale=1"/>
|
||||
|
@ -11,7 +9,6 @@
|
|||
<link rel="stylesheet" href="{base_url}/assets/open-sans/open-sans.css"/>
|
||||
<link rel="stylesheet" href="{base_url}/css/style.css"/>
|
||||
<title>{$config->appName}{if isset($title)} - {$title|escape}{/if}</title>
|
||||
<link rel="canonical" href="{$canonical}"/>
|
||||
<link rel="icon" href="{base_url}/img/favicon.png"/>
|
||||
<meta property="og:title" content="{$config->appName}{if isset($title)} - {$title|escape}{/if}"/>
|
||||
<meta property="og:image" content="{base_url}/img/logo.png"/>
|
||||
|
@ -22,6 +19,8 @@
|
|||
<meta name="theme-color" content="#4F4F4F"/>
|
||||
<link rel="manifest" href="{base_url}/resources/manifest.json"/>
|
||||
<meta name="generator" content="AllTube Download ({$config->getAppVersion()})"/>
|
||||
|
||||
{if isset($debug_render)}
|
||||
{$debug_render->renderHead()}
|
||||
{/if}
|
||||
</head>
|
||||
<body>
|
||||
<div class="page {$class}">
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
{if isset($supportedLocales) AND count($supportedLocales) > 1}
|
||||
<div class="locales small-font">
|
||||
<button class="localesBtn small-font" title="{t}Switch language{/t}">
|
||||
{if isset($locale) AND $locale->getCountry()}
|
||||
{$locale->getCountry()->getEmoji()}
|
||||
{if $locale->getLocale()->getCountry()}
|
||||
{$locale->getLocale()->getCountry()->getEmoji()}
|
||||
{else}
|
||||
{t}Set language{/t}
|
||||
{/if}
|
||||
|
@ -18,7 +18,7 @@
|
|||
{if $supportedLocale->getCountry()}
|
||||
{$supportedLocale->getCountry()->getEmoji()}
|
||||
{/if}
|
||||
{$supportedLocale->getFullName()|ucfirst}
|
||||
{$supportedLocale->getFullName()}
|
||||
</a>
|
||||
</li>
|
||||
{/if}
|
||||
|
@ -27,4 +27,3 @@
|
|||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
<div class="wrapper">
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<h1 class="logobis">
|
||||
<a class="logocompatible" href="{base_url}">
|
||||
<span class="logocompatiblemask"><img src="{base_url}/img/logocompatiblemask.png" width="447" height="107"
|
||||
alt="{$config->appName}"/></span>
|
||||
</a></h1>
|
||||
<a class="logocompatible" href="{path_for name="index"}">
|
||||
<span class="logocompatiblemask">
|
||||
{html_image file='img/logocompatiblemask.png' path_prefix={base_url}|cat:'/' alt=$config->appName}
|
||||
</span>
|
||||
</a>
|
||||
</h1>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
{include file='inc/head.tpl'}
|
||||
{include file='inc/header.tpl'}
|
||||
<main class="main">
|
||||
<div><img class="logo" src="{base_url}/img/logo.png"
|
||||
alt="{$config->appName}" width="328" height="284"></div>
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div>
|
||||
{html_image file='img/logo.png' path_prefix={base_url}|cat:'/' alt=$config->appName class="logo"}
|
||||
</div>
|
||||
<form action="{path_for name="info"}">
|
||||
<label class="labelurl" for="url">
|
||||
{t}Copy here the URL of your video (Youtube, Dailymotion, etc.){/t}
|
||||
{t}Copy here the URL of your video (YouTube, Dailymotion, etc.){/t}
|
||||
</label>
|
||||
<div class="champs">
|
||||
<span class="URLinput_wrapper">
|
||||
<!-- We used to have an autofocus attribute on this field but it triggerd a very specific CSS bug: https://github.com/Rudloff/alltube/issues/117 -->
|
||||
<input class="URLinput large-font" type="url" name="url" id="url"
|
||||
required placeholder="http://example.com/video"/>
|
||||
required placeholder="https://example.com/video"/>
|
||||
</span>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="info"/>
|
||||
|
@ -20,18 +20,23 @@
|
|||
{if $config->convert}
|
||||
<div class="mp3 small-font">
|
||||
<div class="mp3-inner">
|
||||
<input type="checkbox" id="audio" class="audio" name="audio" {($config->defaultAudio) ? 'checked' : ''}>
|
||||
<input type="checkbox" id="audio" class="audio"
|
||||
name="audio" {($config->defaultAudio) ? 'checked' : ''}>
|
||||
<label for="audio"><span class="ui"></span>
|
||||
{t}Audio only (MP3){/t}
|
||||
</label>
|
||||
{if $config->convertSeek}
|
||||
<div class="seekOptions">
|
||||
<label for="from">{t}From{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
||||
placeholder="HH:MM:SS" value="" name="from"
|
||||
id="from"/>
|
||||
<label for="to">{t}to{/t}</label> <input type="text" pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
||||
placeholder="HH:MM:SS" value="" name="to" id="to"/>
|
||||
</div>
|
||||
{if $config->convertSeek}
|
||||
<div class="seekOptions">
|
||||
<label for="from">{t}From{/t}</label> <input type="text"
|
||||
pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
||||
placeholder="HH:MM:SS" value=""
|
||||
name="from"
|
||||
id="from"/>
|
||||
<label for="to">{t}to{/t}</label> <input type="text"
|
||||
pattern="(\d+:)?(\d+:)?\d+(\.\d+)?"
|
||||
placeholder="HH:MM:SS" value="" name="to"
|
||||
id="to"/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,6 +49,4 @@
|
|||
<a class="bookmarklet small-font"
|
||||
href="javascript:window.location='{$domain}{path_for name='info' queryParams=['url' => '%url%']}'.replace('%url%', encodeURIComponent(location.href));">{t}Bookmarklet{/t}</a>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
{/block}
|
||||
|
|
|
@ -1,111 +1,59 @@
|
|||
{include file="inc/head.tpl"}
|
||||
<div class="wrapper">
|
||||
<div itemscope itemtype="http://schema.org/VideoObject">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
{$title="<i itemprop='name'>
|
||||
<a itemprop='url' id='video_link'
|
||||
href='{$video->webpage_url}'>
|
||||
{$video->title}</a></i>"}
|
||||
<p id="download_intro">
|
||||
{t params=['@title' => $title]}You are going to download @title.{/t}
|
||||
</p>
|
||||
{if isset($video->thumbnail)}
|
||||
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt=""/>
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
<div itemscope itemtype="https://schema.org/VideoObject">
|
||||
{include file="inc/logo.tpl"}
|
||||
{include file='snippets/title.tpl' assign=title}
|
||||
<p id="download_intro">
|
||||
{t params=['@title' => $title]}You are going to download @title.{/t}
|
||||
</p>
|
||||
{if isset($video->thumbnail)}
|
||||
{html_image file=$video->thumbnail itemprop="thumbnailUrl" class="thumb"}
|
||||
{/if}
|
||||
{if isset($video->description)}
|
||||
<meta itemprop="description" content="{$video->description|escape}"/>
|
||||
{/if}
|
||||
{if isset($video->upload_date)}
|
||||
<meta itemprop="uploadDate" content="{$video->upload_date}"/>
|
||||
{/if}
|
||||
<br/>
|
||||
<form action="{path_for name="download"}">
|
||||
<input type="hidden" name="url" value="{$video->webpage_url}"/>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="download"/>
|
||||
{/if}
|
||||
{if isset($video->description)}
|
||||
<meta itemprop="description" content="{$video->description|escape}"/>
|
||||
{if isset($video->formats) && count($video->formats) > 1}
|
||||
<h3><label for="format">{t}Available formats:{/t}</label></h3>
|
||||
{*
|
||||
To make the default generic formats translatable:
|
||||
{t}Best{/t}
|
||||
{t}Remux best video with best audio{/t}
|
||||
{t}Worst{/t}
|
||||
*}
|
||||
{html_options name='format' options=$formats selected=$defaultFormat id="format" class="formats monospace"}
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if isset($video->upload_date)}
|
||||
<meta itemprop="uploadDate" content="{$video->upload_date}"/>
|
||||
{if $config->stream}
|
||||
<input type="checkbox" {if $config->stream !== 'ask'}checked{/if} name="stream" id="stream"/>
|
||||
<label for="stream">{t}Stream the video through the server{/t}</label>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<br/>
|
||||
<form action="{path_for name="download"}">
|
||||
<input type="hidden" name="url" value="{$video->webpage_url}"/>
|
||||
{if $config->uglyUrls}
|
||||
<input type="hidden" name="page" value="download"/>
|
||||
{/if}
|
||||
{if isset($video->formats) && count($video->formats) > 1}
|
||||
<h3><label for="format">{t}Available formats:{/t}</label></h3>
|
||||
<select name="format" id="format" class="formats monospace">
|
||||
<optgroup label="{t}Generic formats{/t}">
|
||||
{foreach $config->genericFormats as $format => $name}
|
||||
{*
|
||||
To make the default generic formats translatable:
|
||||
{t}Best{/t}
|
||||
{t}Remux best video with best audio{/t}
|
||||
{t}Worst{/t}
|
||||
*}
|
||||
<option value="{$format}">{t}{$name}{/t}</option>
|
||||
{/foreach}
|
||||
</optgroup>
|
||||
<optgroup label="{t}Detailed formats{/t}" class="monospace">
|
||||
{foreach $video->formats as $format}
|
||||
{if $config->stream || $format->protocol|in_array:array('http', 'https')}
|
||||
{strip}
|
||||
<option value="{$format->format_id}">
|
||||
{$format->ext}
|
||||
{for $foo=1 to (5 - ($format->ext|strlen))}
|
||||
|
||||
{/for}
|
||||
{if isset($format->width)}
|
||||
{$format->width}x{$format->height}
|
||||
{for $foo=1 to (10 - (("{$format->width}x{$format->height}")|strlen))}
|
||||
|
||||
{/for}
|
||||
{else}
|
||||
{for $foo=1 to 10}
|
||||
|
||||
{/for}
|
||||
{/if}
|
||||
{if isset($format->filesize)}
|
||||
{($format->filesize/1000000)|round:2} MB
|
||||
{for $foo=1 to (7 - (($format->filesize/1000000)|round:2|strlen))}
|
||||
|
||||
{/for}
|
||||
{else}
|
||||
{for $foo=1 to 10}
|
||||
|
||||
{/for}
|
||||
{/if}
|
||||
{if isset($format->format_note)}
|
||||
{$format->format_note}
|
||||
{/if}
|
||||
({$format->format_id})
|
||||
</option>
|
||||
{/strip}
|
||||
{/if}
|
||||
{/foreach}
|
||||
</optgroup>
|
||||
</select>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if $config->stream}
|
||||
<input type="checkbox" {if $config->stream !== 'ask'}checked{/if} name="stream" id="stream"/>
|
||||
<label for="stream">{t}Stream the video through the server{/t}</label>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
{if $config->convertAdvanced}
|
||||
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||
<select title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}">
|
||||
{foreach $config->convertAdvancedFormats as $format}
|
||||
<option>{$format}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
{t}with{/t}
|
||||
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
|
||||
<input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}"
|
||||
class="customBitrate"
|
||||
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit"/>
|
||||
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/><br/>
|
||||
</form>
|
||||
</main>
|
||||
{if $config->convertAdvanced}
|
||||
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||
{html_options name='customFormat' values=$config->convertAdvancedFormats output=$config->convertAdvancedFormats
|
||||
title="{t}Custom format{/t}" name="customFormat" aria-label="{t}Format to convert to{/t}"}
|
||||
{t}with{/t}
|
||||
<label for="customBitrate" class="sr-only">{t}Bit rate{/t}</label>
|
||||
<input type="number" value="{$config->audioBitrate}" title="{t}Custom bitrate{/t}"
|
||||
class="customBitrate"
|
||||
name="customBitrate" id="customBitrate" aria-describedby="customBitrateUnit"/>
|
||||
<span id="customBitrateUnit">{t}kbit/s audio{/t}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
{/if}
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/><br/>
|
||||
</form>
|
||||
</div>
|
||||
{include file="inc/footer.tpl"}
|
||||
{/block}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html lang="{$locale->getLocale()->getBcp47()}">
|
||||
{include file='inc/head.tpl'}
|
||||
<body>
|
||||
<div class="page {$class}">
|
||||
{include file='inc/header.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{block name="main"}{/block}
|
||||
</main>
|
||||
</div>
|
||||
{include file='inc/footer.tpl'}
|
||||
</div>
|
||||
{if isset($debug_render)}
|
||||
{$debug_render->render()}
|
||||
{/if}
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +1,12 @@
|
|||
{include file='inc/head.tpl'}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}This video is protected{/t}</h2>
|
||||
<p>{t}You need a password in order to download this video.{/t}</p>
|
||||
<form action="" method="POST">
|
||||
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
||||
<input class="URLinput" type="password" name="password" id="password"/>
|
||||
<br/><br/>
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
||||
</form>
|
||||
</main>
|
||||
{include file='inc/footer.tpl'}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file="inc/logo.tpl"}
|
||||
<h2>{t}This video is protected{/t}</h2>
|
||||
<p>{t}You need a password in order to download this video.{/t}</p>
|
||||
<form action="" method="POST">
|
||||
<label class="sr-only" for="password">{t}Video password{/t}</label>
|
||||
<input class="URLinput" type="password" name="password" id="password"/>
|
||||
<br/><br/>
|
||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}"/>
|
||||
</form>
|
||||
{/block}
|
||||
|
|
|
@ -1,44 +1,40 @@
|
|||
{include file="inc/head.tpl"}
|
||||
<div class="wrapper">
|
||||
<main class="main">
|
||||
{include file="inc/logo.tpl"}
|
||||
{extends file='page.tpl'}
|
||||
{block name='main'}
|
||||
{include file="inc/logo.tpl"}
|
||||
|
||||
{if isset($video->title)}
|
||||
{$title="<i>
|
||||
<a href='{$video->webpage_url}'>
|
||||
{$video->title}</a>
|
||||
</i>"}
|
||||
<p>
|
||||
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
||||
</p>
|
||||
{/if}
|
||||
{if isset($video->title)}
|
||||
{include file='snippets/title.tpl' assign=title}
|
||||
<p>
|
||||
{t params=['@title'=>$title]}Videos extracted from @title:{/t}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{if $config->stream}
|
||||
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
||||
{/if}
|
||||
{foreach $video->entries as $entry}
|
||||
<div class="playlist-entry">
|
||||
<h3 class="playlist-entry-title"><a target="_blank" href="{strip}
|
||||
{if $config->stream}
|
||||
<a href="{path_for name="download"}?url={$video->webpage_url}" class="downloadBtn">Download everything</a>
|
||||
{/if}
|
||||
{foreach $video->entries as $entry}
|
||||
<div class="playlist-entry">
|
||||
<h3 class="playlist-entry-title">
|
||||
<a target="_blank" href="{strip}
|
||||
{if isset($entry->ie_key) and $entry->ie_key == Youtube and !filter_var($entry->url, FILTER_VALIDATE_URL)}
|
||||
https://www.youtube.com/watch?v=
|
||||
{/if}
|
||||
{$entry->url}
|
||||
{/strip}">
|
||||
{if !isset($entry->title)}
|
||||
{if $entry->ie_key == YoutubePlaylist}
|
||||
Playlist
|
||||
{else}
|
||||
Video
|
||||
{/if}
|
||||
{if !isset($entry->title)}
|
||||
{if $entry->ie_key == YoutubePlaylist}
|
||||
Playlist
|
||||
{else}
|
||||
{$entry->title}
|
||||
Video
|
||||
{/if}
|
||||
</a></h3>
|
||||
<a target="_blank" class="downloadBtn"
|
||||
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
||||
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
</main>
|
||||
{include file="inc/footer.tpl"}
|
||||
{else}
|
||||
{$entry->title}
|
||||
{/if}
|
||||
</a>
|
||||
</h3>
|
||||
<a target="_blank" class="downloadBtn"
|
||||
href="{path_for name="download"}?url={$entry->url}">{t}Download{/t}</a>
|
||||
<a target="_blank" href="{path_for name="info"}?url={$entry->url}">{t}More options{/t}</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/block}
|
||||
|
|
|
@ -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;
|
||||
|
||||
use OndraM\CiDetector\CiDetector;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Util\Test;
|
||||
|
||||
/**
|
||||
* Abstract class used by every test.
|
||||
|
@ -18,7 +20,7 @@ abstract class BaseTest extends TestCase
|
|||
*
|
||||
* @return string Path to file
|
||||
*/
|
||||
protected function getConfigFile()
|
||||
protected function getConfigFile(): string
|
||||
{
|
||||
return __DIR__ . '/../config/config_test.yml';
|
||||
}
|
||||
|
@ -37,7 +39,11 @@ abstract class BaseTest extends TestCase
|
|||
*/
|
||||
protected function checkRequirements()
|
||||
{
|
||||
$annotations = $this->getAnnotations();
|
||||
$ciDetector = new CiDetector();
|
||||
$annotations = Test::parseTestMethodAnnotations(
|
||||
static::class,
|
||||
$this->getName()
|
||||
);
|
||||
$requires = [];
|
||||
|
||||
if (isset($annotations['class']['requires'])) {
|
||||
|
@ -48,7 +54,7 @@ abstract class BaseTest extends TestCase
|
|||
}
|
||||
|
||||
foreach ($requires as $require) {
|
||||
if ($require == 'download' && getenv('CI')) {
|
||||
if ($require == 'download' && $ciDetector->isCiDetected()) {
|
||||
$this->markTestSkipped('Do not run tests that download videos on CI.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ use Alltube\Exception\ConfigException;
|
|||
*/
|
||||
class ConfigTest extends BaseTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Test the getInstance function.
|
||||
*
|
||||
|
|
|
@ -40,6 +40,7 @@ abstract class ContainerTest extends BaseTest
|
|||
$this->checkRequirements();
|
||||
|
||||
$this->container = new Container(['environment' => Environment::mock()]);
|
||||
$this->container['root_path'] = dirname(__DIR__);
|
||||
$this->container['config'] = Config::fromFile($this->getConfigFile());
|
||||
$this->container['session'] = SessionFactory::create($this->container);
|
||||
$this->container['locale'] = LocaleManagerFactory::create($this->container);
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
|
||||
namespace Alltube\Test;
|
||||
|
||||
use Alltube\Locale;
|
||||
use Alltube\Controller\BaseController;
|
||||
use Alltube\Controller\DownloadController;
|
||||
use Alltube\Controller\FrontController;
|
||||
use Alltube\Exception\ConfigException;
|
||||
use Alltube\Exception\DependencyException;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Views\Smarty;
|
||||
use SmartyException;
|
||||
|
||||
/**
|
||||
|
@ -34,6 +36,8 @@ abstract class ControllerTest extends ContainerTest
|
|||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->container->get('locale')->setLocale(new Locale('en_US'));
|
||||
|
||||
$frontController = new FrontController($this->container);
|
||||
$downloadController = new DownloadController($this->container);
|
||||
|
||||
|
@ -49,6 +53,12 @@ abstract class ControllerTest extends ContainerTest
|
|||
->setName('locale');
|
||||
$router->map(['GET'], '/redirect', [$downloadController, '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
|
||||
*/
|
||||
protected function getRequestResult(string $request, array $params)
|
||||
protected function getRequestResult(string $request, array $params): Response
|
||||
{
|
||||
return $this->controller->$request(
|
||||
$this->container->get('request')->withQueryParams($params),
|
||||
|
|
|
@ -17,7 +17,6 @@ use SmartyException;
|
|||
*/
|
||||
class ConvertedPlaylistArchiveStreamTest extends StreamTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Prepare tests.
|
||||
*
|
||||
|
|
|
@ -108,14 +108,6 @@ class DownloadControllerTest extends ControllerTest
|
|||
public function testDownloadWithRtmpStream()
|
||||
{
|
||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||
|
||||
$config = $this->container->get('config');
|
||||
$config->setOptions(['stream' => true]);
|
||||
|
||||
$this->assertRequestIsOk(
|
||||
'download',
|
||||
['url' => 'http://www.rtvnh.nl/video/131946', 'format' => 'rtmp-264']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +153,7 @@ class DownloadControllerTest extends ControllerTest
|
|||
*/
|
||||
public function testDownloadWithMissingPassword()
|
||||
{
|
||||
$this->assertRequestIsClientError('download', ['url' => 'http://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('download', ['url' => 'https://vimeo.com/68375962']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,7 +164,7 @@ class DownloadControllerTest extends ControllerTest
|
|||
public function testDownloadWithError()
|
||||
{
|
||||
$this->expectException(YoutubedlException::class);
|
||||
$this->getRequestResult('download', ['url' => 'http://example.com/foo']);
|
||||
$this->getRequestResult('download', ['url' => 'https://example.com/foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ use Alltube\Exception\ConfigException;
|
|||
use Alltube\Exception\DependencyException;
|
||||
use Alltube\Library\Exception\AlltubeLibraryException;
|
||||
use Exception;
|
||||
use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Request;
|
||||
use SmartyException;
|
||||
|
@ -113,7 +114,8 @@ class FrontControllerTest extends ControllerTest
|
|||
*/
|
||||
public function testInfoWithoutUrl()
|
||||
{
|
||||
$this->assertRequestIsRedirect('info');
|
||||
$this->expectException(InvalidURLException::class);
|
||||
$this->getRequestResult('info', []);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,12 +186,12 @@ class FrontControllerTest extends ControllerTest
|
|||
*
|
||||
* @return void
|
||||
* @requires download
|
||||
* @throws AlltubeLibraryException
|
||||
* @throws AlltubeLibraryException|InvalidURLException
|
||||
*/
|
||||
public function testInfoWithPassword()
|
||||
{
|
||||
$result = $this->controller->info(
|
||||
$this->container->get('request')->withQueryParams(['url' => 'http://vimeo.com/68375962'])
|
||||
$this->container->get('request')->withQueryParams(['url' => 'https://vimeo.com/68375962'])
|
||||
->withParsedBody(['password' => 'youtube-dl']),
|
||||
$this->container->get('response')
|
||||
);
|
||||
|
@ -204,8 +206,8 @@ class FrontControllerTest extends ControllerTest
|
|||
*/
|
||||
public function testInfoWithMissingPassword()
|
||||
{
|
||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962']);
|
||||
$this->assertRequestIsClientError('info', ['url' => 'https://vimeo.com/68375962', 'audio' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,7 +49,7 @@ class JsonControllerTest extends ControllerTest
|
|||
public function testJsonWithError()
|
||||
{
|
||||
$this->expectException(YoutubedlException::class);
|
||||
$this->getRequestResult('json', ['url' => 'http://example.com/foo']);
|
||||
$this->getRequestResult('json', ['url' => 'https://example.com/foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,26 +85,32 @@ class LocaleMiddlewareTest extends ContainerTest
|
|||
* Check that the request contains an Accept-Language header.
|
||||
*
|
||||
* @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');
|
||||
$this->assertEquals('foo-BAR', $header[0]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the request contains no Accept-Language header.
|
||||
*
|
||||
* @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');
|
||||
$this->assertEmpty($header);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,7 +54,7 @@ class LocaleTest extends ContainerTest
|
|||
*/
|
||||
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();
|
||||
|
||||
$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(
|
||||
[
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'QUERY_STRING' => 'page=foo',
|
||||
'QUERY_STRING' => 'page=foo',
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -70,7 +79,7 @@ class UglyRouterTest extends ContainerTest
|
|||
public function testPathFor()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'/?page=foo',
|
||||
'/?page=%2Ffoo',
|
||||
$this->router->pathFor('foo', [], [])
|
||||
);
|
||||
}
|
||||
|
@ -84,7 +93,7 @@ class UglyRouterTest extends ContainerTest
|
|||
{
|
||||
$this->router->setBasePath('/bar');
|
||||
$this->assertEquals(
|
||||
'/bar/?page=foo',
|
||||
'/bar/?page=%2Ffoo',
|
||||
$this->router->pathFor('foo', [], [])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ class VideoTest extends ContainerTest
|
|||
*/
|
||||
public function testgetUrlWithPassword()
|
||||
{
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'youtube-dl');
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', 'best', 'youtube-dl');
|
||||
foreach ($video->getUrl() as $videoURL) {
|
||||
$this->assertStringContainsString('vimeocdn.com', $videoURL);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class VideoTest extends ContainerTest
|
|||
public function testgetUrlWithMissingPassword()
|
||||
{
|
||||
$this->expectException(PasswordException::class);
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', $this->format);
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', $this->format);
|
||||
$video->getUrl();
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ class VideoTest extends ContainerTest
|
|||
public function testgetUrlWithWrongPassword()
|
||||
{
|
||||
$this->expectException(WrongPasswordException::class);
|
||||
$video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'foo');
|
||||
$video = new Video($this->downloader, 'https://vimeo.com/68375962', 'best', 'foo');
|
||||
$video->getUrl();
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ class VideoTest extends ContainerTest
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function urlProvider()
|
||||
public function urlProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
|
@ -174,7 +174,7 @@ class VideoTest extends ContainerTest
|
|||
'googlevideo.com',
|
||||
],
|
||||
[
|
||||
'http://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
||||
'https://www.bbc.co.uk/programmes/b039g8p7', 'bestaudio/best',
|
||||
'Kaleidoscope_Leonard_Cohen-b039d07m',
|
||||
'flv',
|
||||
'bbcodspdns.fcod.llnwd.net',
|
||||
|
@ -193,7 +193,7 @@ class VideoTest extends ContainerTest
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function remuxUrlProvider()
|
||||
public function remuxUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
|
@ -210,7 +210,7 @@ class VideoTest extends ContainerTest
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function m3uUrlProvider()
|
||||
public function m3uUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
|
@ -227,7 +227,7 @@ class VideoTest extends ContainerTest
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function rtmpUrlProvider()
|
||||
public function rtmpUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
|
@ -244,10 +244,10 @@ class VideoTest extends ContainerTest
|
|||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function errorUrlProvider()
|
||||
public function errorUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
['http://example.com/video'],
|
||||
['https://example.com/video'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -479,16 +479,11 @@ class VideoTest extends ContainerTest
|
|||
* @param string $format Format
|
||||
*
|
||||
* @return void
|
||||
* @throws AlltubeLibraryException
|
||||
* @dataProvider rtmpUrlProvider
|
||||
*/
|
||||
public function testGetRtmpStream(string $url, string $format)
|
||||
{
|
||||
$this->markTestIncomplete('We need to find another RTMP video.');
|
||||
|
||||
$video = new Video($this->downloader, $url, $format);
|
||||
|
||||
$this->assertStream($this->downloader->getRtmpStream($video));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue