From 5c2823e3f1bc8605830b41a51f0d3f6ca5054844 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Jun 2020 01:44:20 +0200 Subject: [PATCH 1/2] Move Video class to a separate library + improve error handling + youtube-dl update --- README.md | 28 +- classes/Config.php | 54 +- classes/UglyRouter.php | 1 - classes/Video.php | 647 ------------------ classes/exceptions/ConfigException.php | 10 + classes/exceptions/EmptyUrlException.php | 16 - classes/exceptions/PasswordException.php | 16 - .../ConvertedPlaylistArchiveStream.php | 8 +- classes/streams/PlaylistArchiveStream.php | 28 +- classes/streams/YoutubeStream.php | 16 +- composer.json | 9 +- composer.lock | 81 ++- controllers/BaseController.php | 28 +- controllers/DownloadController.php | 156 +++-- controllers/FrontController.php | 65 +- controllers/JsonController.php | 19 +- i18n/template.pot | 227 +++--- templates/error.tpl | 2 +- tests/BaseTest.php | 4 +- tests/ConfigTest.php | 18 +- tests/ControllerTest.php | 5 +- tests/ConvertedPlaylistArchiveStreamTest.php | 12 +- tests/DownloadControllerTest.php | 29 +- tests/FrontControllerTest.php | 22 +- tests/JsonControllerTest.php | 9 +- tests/PlaylistArchiveStreamTest.php | 12 +- tests/VideoStubsTest.php | 54 +- tests/VideoTest.php | 198 +++--- tests/YoutubeChunkStreamTest.php | 14 +- tests/YoutubeStreamTest.php | 13 +- 30 files changed, 649 insertions(+), 1152 deletions(-) delete mode 100644 classes/Video.php create mode 100644 classes/exceptions/ConfigException.php delete mode 100644 classes/exceptions/EmptyUrlException.php delete mode 100644 classes/exceptions/PasswordException.php diff --git a/README.md b/README.md index a310a85..9b60df9 100644 --- a/README.md +++ b/README.md @@ -148,32 +148,8 @@ so that it points to your ffmpeg/avconv binary (`/usr/bin/avconv` on Debian/Ubun ## Use as library -AllTube can also be used as a library to extract a video URL from a webpage. - -You can install it with: - -```bash -composer require rudloff/alltube -``` - -You can then use it in your PHP code: - -```php -use Alltube\Config; -use Alltube\Video; - -require_once __DIR__.'/vendor/autoload.php'; - -Config::setOptions( - [ - 'youtubedl' => '/usr/local/bin/youtube-dl', - ] -); -$video = new Video('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); -$video->getUrl(); -``` - -You can also have a look at this [example project](https://github.com/Rudloff/alltube-example-project). +The `Video` class is now available as [a separate package](https://packagist.org/packages/rudloff/alltube-library) +so that you can reuse it in your projects. ## JSON API diff --git a/classes/Config.php b/classes/Config.php index e308218..337192a 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -6,7 +6,8 @@ namespace Alltube; -use Exception; +use Alltube\Exception\ConfigException; +use Alltube\Library\Downloader; use Jawira\CaseConverter\CaseConverterException; use Symfony\Component\ErrorHandler\Debug; use Symfony\Component\Yaml\Yaml; @@ -141,7 +142,7 @@ class Config * Config constructor. * * @param mixed[] $options Options - * @throws CaseConverterException + * @throws ConfigException */ private function __construct(array $options = []) { @@ -197,20 +198,15 @@ class Config * Throw an exception if some of the options are invalid. * * @return void - * @throws Exception If Python is missing - * - * @throws Exception If youtube-dl is missing + * @throws ConfigException If Python is missing + * @throws ConfigException If youtube-dl is missing */ private function validateOptions() { - /* - We don't translate these exceptions because they usually occur before Slim can catch them - so they will go to the logs. - */ if (!is_file($this->youtubedl)) { - throw new Exception("Can't find youtube-dl at " . $this->youtubedl); - } elseif (!Video::checkCommand([$this->python, '--version'])) { - throw new Exception("Can't find Python at " . $this->python); + throw new ConfigException("Can't find youtube-dl at " . $this->youtubedl); + } elseif (!Downloader::checkCommand([$this->python, '--version'])) { + throw new ConfigException("Can't find Python at " . $this->python); } if (!class_exists(Debug::class)) { @@ -241,13 +237,18 @@ class Config * If the value is an array, you should use the YAML format: "CONVERT_ADVANCED_FORMATS='[foo, bar]'" * * @return void - * @throws CaseConverterException + * @throws ConfigException */ private function getEnv() { foreach (get_object_vars($this) as $prop => $value) { - $convert = new Convert($prop); - $env = getenv($convert->toMacro()); + try { + $convert = new Convert($prop); + $env = getenv($convert->toMacro()); + } catch (CaseConverterException $e) { + // This should not happen. + throw new ConfigException('Could not parse option name: ' . $prop, $e->getCode(), $e); + } if ($env) { $this->$prop = Yaml::parse($env); } @@ -273,7 +274,7 @@ class Config * * @param string $file Path to the YAML file * @return void - * @throws Exception + * @throws ConfigException */ public static function setFile($file) { @@ -282,7 +283,7 @@ class Config self::$instance = new self($options); self::$instance->validateOptions(); } else { - throw new Exception("Can't find config file at " . $file); + throw new ConfigException("Can't find config file at " . $file); } } @@ -292,7 +293,7 @@ class Config * @param mixed[] $options Options (see `config/config.example.yml` for available options) * @param bool $update True to update an existing instance * @return void - * @throws Exception + * @throws ConfigException */ public static function setOptions(array $options, $update = true) { @@ -314,4 +315,21 @@ class Config { self::$instance = null; } + + /** + * Return a downloader object with the current config. + * + * @return Downloader + */ + public function getDownloader() + { + return new Downloader( + $this->youtubedl, + $this->params, + $this->python, + $this->avconv, + $this->phantomjsDir, + $this->avconvVerbosity + ); + } } diff --git a/classes/UglyRouter.php b/classes/UglyRouter.php index f03efe7..a5b1020 100644 --- a/classes/UglyRouter.php +++ b/classes/UglyRouter.php @@ -48,7 +48,6 @@ class UglyRouter extends Router * * @return string * @throws InvalidArgumentException If required data not provided - * * @throws RuntimeException If named route does not exist */ public function pathFor($name, array $data = [], array $queryParams = []) diff --git a/classes/Video.php b/classes/Video.php deleted file mode 100644 index 38c9287..0000000 --- a/classes/Video.php +++ /dev/null @@ -1,647 +0,0 @@ -webpageUrl = $webpageUrl; - $this->requestedFormat = $requestedFormat; - $this->password = $password; - $this->config = Config::getInstance(); - - $this->localeManager = LocaleManager::getInstance(); - } - - /** - * Return a youtube-dl process with the specified arguments. - * - * @param string[] $arguments Arguments - * - * @return Process - */ - private static function getProcess(array $arguments) - { - $config = Config::getInstance(); - - return new Process( - array_merge( - [$config->python, $config->youtubedl], - $config->params, - $arguments - ) - ); - } - - /** - * List all extractors. - * - * @return string[] Extractors - * - * @throws PasswordException - */ - public static function getExtractors() - { - $video = new self(''); - - return explode("\n", trim($video->callYoutubedl(['--list-extractors']))); - } - - /** - * Call youtube-dl. - * - * @param string[] $arguments Arguments - * - * @return string Result - * @throws Exception If the password is wrong - * @throws Exception If youtube-dl returns an error - * - * @throws PasswordException If the video is protected by a password and no password was specified - */ - private function callYoutubedl(array $arguments) - { - $config = Config::getInstance(); - - $process = self::getProcess($arguments); - //This is needed by the openload extractor because it runs PhantomJS - $process->setEnv(['PATH' => $config->phantomjsDir]); - $process->run(); - if (!$process->isSuccessful()) { - $errorOutput = trim($process->getErrorOutput()); - $exitCode = intval($process->getExitCode()); - if ($errorOutput == 'ERROR: This video is protected by a password, use the --video-password option') { - throw new PasswordException($errorOutput, $exitCode); - } elseif (substr($errorOutput, 0, 21) == 'ERROR: Wrong password') { - throw new Exception($this->localeManager->t('Wrong password'), $exitCode); - } else { - throw new Exception($errorOutput, $exitCode); - } - } else { - return trim($process->getOutput()); - } - } - - /** - * Get a property from youtube-dl. - * - * @param string $prop Property - * - * @return string - * @throws PasswordException - */ - private function getProp($prop = 'dump-json') - { - $arguments = ['--' . $prop]; - - if (isset($this->webpageUrl)) { - $arguments[] = $this->webpageUrl; - } - if (isset($this->requestedFormat)) { - $arguments[] = '-f'; - $arguments[] = $this->requestedFormat; - } - if (isset($this->password)) { - $arguments[] = '--video-password'; - $arguments[] = $this->password; - } - - return $this->callYoutubedl($arguments); - } - - /** - * Get all information about a video. - * - * @return stdClass Decoded JSON - * - * @throws PasswordException - */ - public function getJson() - { - if (!isset($this->json)) { - $this->json = json_decode($this->getProp('dump-single-json')); - } - - return $this->json; - } - - /** - * Magic method to get a property from the JSON object returned by youtube-dl. - * - * @param string $name Property - * - * @return mixed - * @throws PasswordException - */ - public function __get($name) - { - if (isset($this->$name)) { - return $this->getJson()->$name; - } - - return null; - } - - /** - * Magic method to check if the JSON object returned by youtube-dl has a property. - * - * @param string $name Property - * - * @return bool - * @throws PasswordException - */ - public function __isset($name) - { - return isset($this->getJson()->$name); - } - - /** - * Get URL of video from URL of page. - * - * It generally returns only one URL. - * But it can return two URLs when multiple formats are specified - * (eg. bestvideo+bestaudio). - * - * @return string[] URLs of video - * @throws EmptyUrlException - * @throws PasswordException - */ - public function getUrl() - { - // Cache the URLs. - if (!isset($this->urls)) { - $this->urls = explode("\n", $this->getProp('get-url')); - - if (empty($this->urls[0])) { - throw new EmptyUrlException($this->localeManager->t('youtube-dl returned an empty URL.')); - } - } - - return $this->urls; - } - - /** - * Get filename of video file from URL of page. - * - * @return string Filename of extracted video - * - * @throws PasswordException - */ - public function getFilename() - { - return trim($this->getProp('get-filename')); - } - - /** - * Get filename of video with the specified extension. - * - * @param string $extension New file extension - * - * @return string Filename of extracted video with specified extension - * @throws PasswordException - */ - public function getFileNameWithExtension($extension) - { - return str_replace('.' . $this->ext, '.' . $extension, $this->getFilename()); - } - - /** - * Return arguments used to run rtmp for a specific video. - * - * @return string[] Arguments - */ - private function getRtmpArguments() - { - $arguments = []; - - if ($this->protocol == 'rtmp') { - foreach ( - [ - 'url' => '-rtmp_tcurl', - 'webpage_url' => '-rtmp_pageurl', - 'player_url' => '-rtmp_swfverify', - 'flash_version' => '-rtmp_flashver', - 'play_path' => '-rtmp_playpath', - 'app' => '-rtmp_app', - ] as $property => $option - ) { - if (isset($this->{$property})) { - $arguments[] = $option; - $arguments[] = $this->{$property}; - } - } - - if (isset($this->rtmp_conn)) { - foreach ($this->rtmp_conn as $conn) { - $arguments[] = '-rtmp_conn'; - $arguments[] = $conn; - } - } - } - - return $arguments; - } - - /** - * Check if a command runs successfully. - * - * @param string[] $command Command and arguments - * - * @return bool False if the command returns an error, true otherwise - */ - public static function checkCommand(array $command) - { - $process = new Process($command); - $process->run(); - - return $process->isSuccessful(); - } - - /** - * Get a process that runs avconv in order to convert a video. - * - * @param int $audioBitrate Audio bitrate of the converted file - * @param string $filetype Filetype of the converted file - * @param bool $audioOnly True to return an audio-only file - * @param string $from Start the conversion at this time - * @param string $to End the conversion at this time - * - * @return Process Process - * @throws Exception If avconv/ffmpeg is missing - * - */ - private function getAvconvProcess( - $audioBitrate, - $filetype = 'mp3', - $audioOnly = true, - $from = null, - $to = null - ) { - if (!$this->checkCommand([$this->config->avconv, '-version'])) { - throw new Exception( - $this->localeManager->t( - "Can't find avconv or ffmpeg at @path.", - ['@path' => $this->config->avconv] - ) - ); - } - - $durationRegex = '/(\d+:)?(\d+:)?(\d+)/'; - - $afterArguments = []; - - if ($audioOnly) { - $afterArguments[] = '-vn'; - } - - if (!empty($from)) { - if (!preg_match($durationRegex, $from)) { - throw new Exception($this->localeManager->t('Invalid start time: @from.', ['@from' => $from])); - } - $afterArguments[] = '-ss'; - $afterArguments[] = $from; - } - if (!empty($to)) { - if (!preg_match($durationRegex, $to)) { - throw new Exception($this->localeManager->t('Invalid end time: @to.', ['@to' => $to])); - } - $afterArguments[] = '-to'; - $afterArguments[] = $to; - } - - $urls = $this->getUrl(); - - $arguments = array_merge( - [ - $this->config->avconv, - '-v', $this->config->avconvVerbosity, - ], - $this->getRtmpArguments(), - [ - '-i', $urls[0], - '-f', $filetype, - '-b:a', $audioBitrate . 'k', - ], - $afterArguments, - [ - 'pipe:1', - ] - ); - - //Vimeo needs a correct user-agent - $arguments[] = '-user_agent'; - $arguments[] = $this->getProp('dump-user-agent'); - - return new Process($arguments); - } - - /** - * Get audio stream of converted video. - * - * @param string $from Start the conversion at this time - * @param string $to End the conversion at this time - * - * @return resource popen stream - * @throws Exception If the popen stream was not created correctly - * - * @throws Exception If your try to convert an M3U8 video - */ - public function getAudioStream($from = null, $to = null) - { - if (isset($this->_type) && $this->_type == 'playlist') { - throw new Exception($this->localeManager->t('Conversion of playlists is not supported.')); - } - - if (isset($this->protocol)) { - if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { - throw new Exception($this->localeManager->t('Conversion of M3U8 files is not supported.')); - } elseif ($this->protocol == 'http_dash_segments') { - throw new Exception($this->localeManager->t('Conversion of DASH segments is not supported.')); - } - } - - $avconvProc = $this->getAvconvProcess($this->config->audioBitrate, 'mp3', true, $from, $to); - - $stream = popen($avconvProc->getCommandLine(), 'r'); - - if (!is_resource($stream)) { - throw new Exception($this->localeManager->t('Could not open popen stream.')); - } - - return $stream; - } - - /** - * Get video stream from an M3U playlist. - * - * @return resource popen stream - * @throws Exception If the popen stream was not created correctly - * - * @throws Exception If avconv/ffmpeg is missing - */ - public function getM3uStream() - { - if (!$this->checkCommand([$this->config->avconv, '-version'])) { - throw new Exception( - $this->localeManager->t( - "Can't find avconv or ffmpeg at @path.", - ['@path' => $this->config->avconv] - ) - ); - } - - $urls = $this->getUrl(); - - $process = new Process( - [ - $this->config->avconv, - '-v', $this->config->avconvVerbosity, - '-i', $urls[0], - '-f', $this->ext, - '-c', 'copy', - '-bsf:a', 'aac_adtstoasc', - '-movflags', 'frag_keyframe+empty_moov', - 'pipe:1', - ] - ); - - $stream = popen($process->getCommandLine(), 'r'); - if (!is_resource($stream)) { - throw new Exception($this->localeManager->t('Could not open popen stream.')); - } - - return $stream; - } - - /** - * Get an avconv stream to remux audio and video. - * - * @return resource popen stream - * @throws Exception If the popen stream was not created correctly - * - */ - public function getRemuxStream() - { - $urls = $this->getUrl(); - - if (!isset($urls[0]) || !isset($urls[1])) { - throw new Exception($this->localeManager->t('This video does not have two URLs.')); - } - - $process = new Process( - [ - $this->config->avconv, - '-v', $this->config->avconvVerbosity, - '-i', $urls[0], - '-i', $urls[1], - '-c', 'copy', - '-map', '0:v:0', - '-map', '1:a:0', - '-f', 'matroska', - 'pipe:1', - ] - ); - - $stream = popen($process->getCommandLine(), 'r'); - if (!is_resource($stream)) { - throw new Exception($this->localeManager->t('Could not open popen stream.')); - } - - return $stream; - } - - /** - * Get video stream from an RTMP video. - * - * @return resource popen stream - * @throws Exception If the popen stream was not created correctly - * - */ - public function getRtmpStream() - { - $urls = $this->getUrl(); - - $process = new Process( - array_merge( - [ - $this->config->avconv, - '-v', $this->config->avconvVerbosity, - ], - $this->getRtmpArguments(), - [ - '-i', $urls[0], - '-f', $this->ext, - 'pipe:1', - ] - ) - ); - $stream = popen($process->getCommandLine(), 'r'); - if (!is_resource($stream)) { - throw new Exception($this->localeManager->t('Could not open popen stream.')); - } - - return $stream; - } - - /** - * Get the stream of a converted video. - * - * @param int $audioBitrate Audio bitrate of the converted file - * @param string $filetype Filetype of the converted file - * - * @return resource popen stream - * @throws Exception If the popen stream was not created correctly - * - * @throws Exception If your try to convert and M3U8 video - */ - public function getConvertedStream($audioBitrate, $filetype) - { - if (in_array($this->protocol, ['m3u8', 'm3u8_native'])) { - throw new Exception($this->localeManager->t('Conversion of M3U8 files is not supported.')); - } - - $avconvProc = $this->getAvconvProcess($audioBitrate, $filetype, false); - - $stream = popen($avconvProc->getCommandLine(), 'r'); - - if (!is_resource($stream)) { - throw new Exception($this->localeManager->t('Could not open popen stream.')); - } - - return $stream; - } - - /** - * Get the same video but with another format. - * - * @param string $format New format - * - * @return Video - */ - public function withFormat($format) - { - return new self($this->webpageUrl, $format, $this->password); - } - - /** - * Get a HTTP response containing the video. - * - * @param mixed[] $headers HTTP headers of the request - * - * @return ResponseInterface - * @throws EmptyUrlException - * @throws PasswordException - * @link https://github.com/guzzle/guzzle/issues/2640 - */ - public function getHttpResponse(array $headers = []) - { - // IDN conversion breaks with Google hosts like https://r3---sn-25glene6.googlevideo.com/. - $client = new Client(['idn_conversion' => false]); - $urls = $this->getUrl(); - $stream_context_options = []; - - if (array_key_exists('Referer', (array)$this->http_headers)) { - $stream_context_options = [ - 'http' => [ - 'header' => 'Referer: ' . $this->http_headers->Referer - ] - ]; - } - - return $client->request( - 'GET', - $urls[0], - [ - 'stream' => true, - 'stream_context' => $stream_context_options, - 'headers' => array_merge((array)$this->http_headers, $headers) - ] - ); - } -} diff --git a/classes/exceptions/ConfigException.php b/classes/exceptions/ConfigException.php new file mode 100644 index 0000000..f533b35 --- /dev/null +++ b/classes/exceptions/ConfigException.php @@ -0,0 +1,10 @@ +curVideoStream = new Stream($video->getAudioStream()); + $this->curVideoStream = new Stream($this->downloader->getAudioStream($video)); $this->init_file_stream_transfer( $video->getFileNameWithExtension('mp3'), diff --git a/classes/streams/PlaylistArchiveStream.php b/classes/streams/PlaylistArchiveStream.php index 2e682fb..d55e554 100644 --- a/classes/streams/PlaylistArchiveStream.php +++ b/classes/streams/PlaylistArchiveStream.php @@ -6,9 +6,9 @@ namespace Alltube\Stream; -use Alltube\Exception\EmptyUrlException; -use Alltube\Exception\PasswordException; -use Alltube\Video; +use Alltube\Library\Downloader; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Video; use Barracuda\ArchiveStream\ZipArchive; use Psr\Http\Message\StreamInterface; @@ -47,22 +47,32 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface */ private $isComplete = false; + /** + * Downloader object. + * + * @var Downloader + */ + protected $downloader; + /** * PlaylistArchiveStream constructor. * * We don't call the parent constructor because it messes up the output buffering. * + * @param Downloader $downloader Downloader object * @param Video $video Video/playlist to download * @noinspection PhpMissingParentConstructorInspection */ - public function __construct(Video $video) + public function __construct(Downloader $downloader, Video $video) { + $this->downloader = $downloader; + $buffer = fopen('php://temp', 'r+'); if ($buffer !== false) { $this->buffer = $buffer; } foreach ($video->entries as $entry) { - $this->videos[] = new Video($entry->url); + $this->videos[] = $downloader->getVideo($entry->url); } } @@ -244,12 +254,11 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface * @param Video $video Video to stream * * @return void - * @throws PasswordException - * @throws EmptyUrlException + * @throws AlltubeLibraryException */ protected function startVideoStream(Video $video) { - $response = $video->getHttpResponse(); + $response = $this->downloader->getHttpResponse($video); $this->curVideoStream = $response->getBody(); $contentLengthHeaders = $response->getHeader('Content-Length'); @@ -266,8 +275,7 @@ class PlaylistArchiveStream extends ZipArchive implements StreamInterface * @param int $count Number of bytes to read * * @return string|false - * @throws EmptyUrlException - * @throws PasswordException + * @throws AlltubeLibraryException */ public function read($count) { diff --git a/classes/streams/YoutubeStream.php b/classes/streams/YoutubeStream.php index 3995d11..fdd19dd 100644 --- a/classes/streams/YoutubeStream.php +++ b/classes/streams/YoutubeStream.php @@ -6,9 +6,9 @@ namespace Alltube\Stream; -use Alltube\Exception\EmptyUrlException; -use Alltube\Exception\PasswordException; -use Alltube\Video; +use Alltube\Library\Downloader; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Video; use GuzzleHttp\Psr7\AppendStream; /** @@ -20,15 +20,15 @@ class YoutubeStream extends AppendStream /** * YoutubeStream constructor. * + * @param Downloader $downloader Downloader object * @param Video $video Video to stream - * @throws EmptyUrlException - * @throws PasswordException + * @throws AlltubeLibraryException */ - public function __construct(Video $video) + public function __construct(Downloader $downloader, Video $video) { parent::__construct(); - $stream = $video->getHttpResponse(); + $stream = $downloader->getHttpResponse($video); $contentLenghtHeader = $stream->getHeader('Content-Length'); $rangeStart = 0; @@ -37,7 +37,7 @@ class YoutubeStream extends AppendStream if ($rangeEnd >= $contentLenghtHeader[0]) { $rangeEnd = intval($contentLenghtHeader[0]) - 1; } - $response = $video->getHttpResponse(['Range' => 'bytes=' . $rangeStart . '-' . $rangeEnd]); + $response = $downloader->getHttpResponse($video, ['Range' => 'bytes=' . $rangeStart . '-' . $rangeEnd]); $this->addStream(new YoutubeChunkStream($response)); $rangeStart = $rangeEnd + 1; } diff --git a/composer.json b/composer.json index 36c437b..83f059b 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,15 @@ "aura/session": "^2.1", "barracudanetworks/archivestream-php": "^1.0", "ffmpeg/ffmpeg": "^4.1", - "guzzlehttp/guzzle": "^6.5", "jawira/case-converter": "^3.4", "mathmarques/smarty-view": "^1.1", "npm-asset/open-sans-fontface": "^1.4", "rinvex/countries": "^6.1", + "rudloff/alltube-library": "^0.1.0", "symfony/finder": "^5.0", - "symfony/process": "^4.0", "symfony/translation": "^4.0", "symfony/yaml": "^4.0", - "ytdl-org/youtube-dl": "^2020.05", + "ytdl-org/youtube-dl": "^2020.06", "zonuexe/http-accept-language": "^0.4.1" }, "require-dev": { @@ -51,10 +50,10 @@ "type": "package", "package": { "name": "ytdl-org/youtube-dl", - "version": "2020.05.08", + "version": "2020.06.16.1", "dist": { "type": "zip", - "url": "https://github.com/ytdl-org/youtube-dl/archive/2020.05.08.zip" + "url": "https://github.com/ytdl-org/youtube-dl/archive/2020.06.16.1.zip" } } }, diff --git a/composer.lock b/composer.lock index c1459b1..d164fd2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5b43d7dd93a519a8edb85623fcffc639", + "content-hash": "dba22d2bff872c8e1310567d4336db16", "packages": [ { "name": "anam/phantomjs-linux-x86-binary", @@ -164,16 +164,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.5.3", + "version": "6.5.5", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e" + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e", - "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "shasum": "" }, "require": { @@ -181,7 +181,7 @@ "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.11" + "symfony/polyfill-intl-idn": "^1.17.0" }, "require-dev": { "ext-curl": "*", @@ -227,7 +227,7 @@ "rest", "web service" ], - "time": "2020-04-18T10:38:46+00:00" + "time": "2020-06-16T21:01:06+00:00" }, { "name": "guzzlehttp/promises", @@ -790,6 +790,47 @@ ], "time": "2020-03-13T18:04:45+00:00" }, + { + "name": "rudloff/alltube-library", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/Rudloff/alltube-library.git", + "reference": "09b47e0cf3157a79724177d6cadac8cee8cae588" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Rudloff/alltube-library/zipball/09b47e0cf3157a79724177d6cadac8cee8cae588", + "reference": "09b47e0cf3157a79724177d6cadac8cee8cae588", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.5", + "symfony/process": "^4.0|^5.0" + }, + "require-dev": { + "phpro/grumphp": "^0.18.0", + "phpstan/phpstan": "^0.12.29", + "roave/security-advisories": "dev-master", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Alltube\\Library\\": "classes/", + "Alltube\\Library\\Exception\\": "classes/exceptions/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-only" + ], + "description": "PHP wrapper for youtube-dl", + "homepage": "http://alltubedownload.net/", + "time": "2020-06-21T12:25:10+00:00" + }, { "name": "slim/slim", "version": "3.12.3", @@ -1146,16 +1187,16 @@ }, { "name": "symfony/polyfill-php72", - "version": "v1.13.1", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038" + "reference": "f048e612a3905f34931127360bdd2def19a5e582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", + "reference": "f048e612a3905f34931127360bdd2def19a5e582", "shasum": "" }, "require": { @@ -1164,7 +1205,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -1197,20 +1238,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/process", - "version": "v4.4.8", + "version": "v4.4.10", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4" + "reference": "c714958428a85c86ab97e3a0c96db4c4f381b7f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4b6a9a4013baa65d409153cbb5a895bf093dc7f4", - "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4", + "url": "https://api.github.com/repos/symfony/process/zipball/c714958428a85c86ab97e3a0c96db4c4f381b7f5", + "reference": "c714958428a85c86ab97e3a0c96db4c4f381b7f5", "shasum": "" }, "require": { @@ -1246,7 +1287,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2020-04-15T15:56:18+00:00" + "time": "2020-05-30T20:06:45+00:00" }, { "name": "symfony/translation", @@ -1442,10 +1483,10 @@ }, { "name": "ytdl-org/youtube-dl", - "version": "2020.05.08", + "version": "2020.06.16.1", "dist": { "type": "zip", - "url": "https://github.com/ytdl-org/youtube-dl/archive/2020.05.08.zip" + "url": "https://github.com/ytdl-org/youtube-dl/archive/2020.06.16.1.zip" }, "type": "library" }, diff --git a/controllers/BaseController.php b/controllers/BaseController.php index bf48a24..05d3eb2 100644 --- a/controllers/BaseController.php +++ b/controllers/BaseController.php @@ -7,12 +7,14 @@ namespace Alltube\Controller; use Alltube\Config; +use Alltube\Library\Downloader; +use Alltube\Library\Video; use Alltube\LocaleManager; use Alltube\SessionManager; -use Alltube\Video; use Aura\Session\Segment; use Psr\Container\ContainerInterface; use Slim\Http\Request; +use Slim\Http\Response; /** * Abstract class used by every controller. @@ -61,6 +63,13 @@ abstract class BaseController */ protected $localeManager; + /** + * Downloader instance. + * + * @var Downloader + */ + protected $downloader; + /** * BaseController constructor. * @@ -73,6 +82,7 @@ abstract class BaseController $session = SessionManager::getSession(); $this->sessionSegment = $session->getSegment(self::class); $this->localeManager = $this->container->get('locale'); + $this->downloader = $this->config->getDownloader(); if (!$this->config->stream) { // Force HTTP if stream is not enabled. @@ -117,4 +127,20 @@ abstract class BaseController return $password; } + + /** + * Display an user-friendly error. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * @param string $message Error message + * + * @return Response HTTP response + */ + protected function displayError(Request $request, Response $response, $message) + { + $controller = new FrontController($this->container); + + return $controller->displayError($request, $response, $message); + } } diff --git a/controllers/DownloadController.php b/controllers/DownloadController.php index 30f1e27..dd3e46f 100644 --- a/controllers/DownloadController.php +++ b/controllers/DownloadController.php @@ -6,13 +6,19 @@ namespace Alltube\Controller; -use Alltube\Exception\EmptyUrlException; -use Alltube\Exception\PasswordException; +use Alltube\Config; +use Alltube\Library\Exception\EmptyUrlException; +use Alltube\Library\Exception\InvalidProtocolConversionException; +use Alltube\Library\Exception\PasswordException; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Exception\PlaylistConversionException; +use Alltube\Library\Exception\PopenStreamException; +use Alltube\Library\Exception\RemuxException; +use Alltube\Library\Exception\WrongPasswordException; +use Alltube\Library\Exception\YoutubedlException; use Alltube\Stream\ConvertedPlaylistArchiveStream; use Alltube\Stream\PlaylistArchiveStream; use Alltube\Stream\YoutubeStream; -use Alltube\Video; -use Exception; use Slim\Http\Request; use Slim\Http\Response; use Slim\Http\Stream; @@ -25,17 +31,18 @@ class DownloadController extends BaseController /** * Redirect to video file. * - * @param Request $request PSR-7 request + * @param Request $request PSR-7 request * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException */ public function download(Request $request, Response $response) { $url = $request->getQueryParam('url'); if (isset($url)) { - $this->video = new Video($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')) { @@ -49,14 +56,33 @@ class DownloadController extends BaseController // Regular download. return $this->getDownloadResponse($request, $response); } catch (PasswordException $e) { - return $response->withRedirect( - $this->container->get('router')->pathFor('info') . - '?' . http_build_query($request->getQueryParams()) - ); - } catch (Exception $e) { - $response->getBody()->write($e->getMessage()); + $frontController = new FrontController($this->container); - return $response->withHeader('Content-Type', 'text/plain')->withStatus(500); + 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 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; + } } } else { return $response->withRedirect($this->container->get('router')->pathFor('index')); @@ -70,8 +96,7 @@ class DownloadController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response - * @throws PasswordException - * @throws Exception + * @throws AlltubeLibraryException */ private function getConvertedAudioResponse(Request $request, Response $response) { @@ -86,13 +111,7 @@ class DownloadController extends BaseController $response = $response->withHeader('Content-Type', 'audio/mpeg'); if ($request->isGet() || $request->isPost()) { - try { - $process = $this->video->getAudioStream($from, $to); - } catch (Exception $e) { - // Fallback to default format. - $this->video = $this->video->withFormat($this->defaultFormat); - $process = $this->video->getAudioStream($from, $to); - } + $process = $this->downloader->getAudioStream($this->video, $this->config->audioBitrate, $from, $to); $response = $response->withBody(new Stream($process)); } @@ -106,36 +125,38 @@ class DownloadController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException + * @throws EmptyUrlException * @throws PasswordException + * @throws WrongPasswordException */ private function getAudioResponse(Request $request, Response $response) { - try { - // First, we try to get a MP3 file directly. - if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { - throw new Exception('Force convert when we need to seek.'); - } - - if ($this->config->stream) { - $this->video = $this->video->withFormat('mp3'); - - return $this->getStream($request, $response); - } else { - $this->video = $this->video->withFormat('mp3[protocol=https]/mp3[protocol=http]'); - - $urls = $this->video->getUrl(); - - return $response->withRedirect($urls[0]); - } - } catch (PasswordException $e) { - $frontController = new FrontController($this->container); - - return $frontController->password($request, $response); - } catch (Exception $e) { - // If MP3 is not available, we convert it. - $this->video = $this->video->withFormat('bestaudio/best'); + if (!empty($request->getQueryParam('from')) || !empty($request->getQueryParam('to'))) { + // Force convert when we need to seek. + $this->video = $this->video->withFormat('bestaudio/' . $this->defaultFormat); return $this->getConvertedAudioResponse($request, $response); + } else { + try { + // First, we try to get a MP3 file directly. + if ($this->config->stream) { + $this->video = $this->video->withFormat('mp3'); + + return $this->getStream($request, $response); + } else { + $this->video = $this->video->withFormat(Config::addHttpToFormat('mp3')); + + $urls = $this->video->getUrl(); + + return $response->withRedirect($urls[0]); + } + } catch (YoutubedlException $e) { + // If MP3 is not available, we convert it. + $this->video = $this->video->withFormat('bestaudio/' . $this->defaultFormat); + + return $this->getConvertedAudioResponse($request, $response); + } } } @@ -146,17 +167,15 @@ class DownloadController extends BaseController * * @param Response $response PSR-7 response * @return Response HTTP response - * @throws EmptyUrlException - * @throws PasswordException - * @throws Exception + * @throws AlltubeLibraryException */ private function getStream(Request $request, Response $response) { if (isset($this->video->entries)) { if ($this->config->convert && $request->getQueryParam('audio')) { - $stream = new ConvertedPlaylistArchiveStream($this->video); + $stream = new ConvertedPlaylistArchiveStream($this->downloader, $this->video); } else { - $stream = new PlaylistArchiveStream($this->video); + $stream = new PlaylistArchiveStream($this->downloader, $this->video); } $response = $response->withHeader('Content-Type', 'application/zip'); $response = $response->withHeader( @@ -167,10 +186,10 @@ class DownloadController extends BaseController return $response->withBody($stream); } elseif ($this->video->protocol == 'rtmp') { $response = $response->withHeader('Content-Type', 'video/' . $this->video->ext); - $body = new Stream($this->video->getRtmpStream()); + $body = new Stream($this->downloader->getRtmpStream($this->video)); } elseif ($this->video->protocol == 'm3u8' || $this->video->protocol == 'm3u8_native') { $response = $response->withHeader('Content-Type', 'video/' . $this->video->ext); - $body = new Stream($this->video->getM3uStream()); + $body = new Stream($this->downloader->getM3uStream($this->video)); } else { $headers = []; $range = $request->getHeader('Range'); @@ -178,7 +197,7 @@ class DownloadController extends BaseController if (!empty($range)) { $headers['Range'] = $range; } - $stream = $this->video->getHttpResponse($headers); + $stream = $this->downloader->getHttpResponse($this->video, $headers); $response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type')); $response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length')); @@ -190,7 +209,7 @@ class DownloadController extends BaseController if (isset($this->video->downloader_options->http_chunk_size)) { // Workaround for Youtube throttling the download speed. - $body = new YoutubeStream($this->video); + $body = new YoutubeStream($this->downloader, $this->video); } else { $body = $stream->getBody(); } @@ -201,7 +220,7 @@ class DownloadController extends BaseController $response = $response->withHeader( 'Content-Disposition', 'attachment; filename="' . - $this->video->getFilename() . '"' + $this->video->getFilename() . '"' ); return $response; @@ -210,19 +229,18 @@ class DownloadController extends BaseController /** * Get a remuxed stream piped through the server. * - * @param Response $response PSR-7 response * @param Request $request PSR-7 request * + * @param Response $response PSR-7 response * @return Response HTTP response - * @throws PasswordException - * @throws Exception + * @throws AlltubeLibraryException */ private function getRemuxStream(Request $request, Response $response) { if (!$this->config->remux) { - throw new Exception($this->localeManager->t('You need to enable remux mode to merge two formats.')); + throw new RemuxException('You need to enable remux mode to merge two formats.'); } - $stream = $this->video->getRemuxStream(); + $stream = $this->downloader->getRemuxStream($this->video); $response = $response->withHeader('Content-Type', 'video/x-matroska'); if ($request->isGet()) { $response = $response->withBody(new Stream($stream)); @@ -242,9 +260,7 @@ class DownloadController extends BaseController * * @param Response $response PSR-7 response * @return Response HTTP response - * @throws EmptyUrlException - * @throws PasswordException - * @throws Exception + * @throws AlltubeLibraryException */ private function getDownloadResponse(Request $request, Response $response) { @@ -263,7 +279,7 @@ class DownloadController extends BaseController return $this->getStream($request, $response); } else { if (empty($videoUrls[0])) { - throw new Exception($this->localeManager->t("Can't find URL of video.")); + throw new EmptyUrlException("Can't find URL of video."); } return $response->withRedirect($videoUrls[0]); @@ -277,8 +293,13 @@ class DownloadController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException + * @throws InvalidProtocolConversionException * @throws PasswordException - * @throws Exception + * @throws PlaylistConversionException + * @throws WrongPasswordException + * @throws YoutubedlException + * @throws PopenStreamException */ private function getConvertedResponse(Request $request, Response $response) { @@ -290,7 +311,8 @@ class DownloadController extends BaseController $response = $response->withHeader('Content-Type', 'video/' . $request->getQueryParam('customFormat')); if ($request->isGet() || $request->isPost()) { - $process = $this->video->getConvertedStream( + $process = $this->downloader->getConvertedStream( + $this->video, $request->getQueryParam('customBitrate'), $request->getQueryParam('customFormat') ); diff --git a/controllers/FrontController.php b/controllers/FrontController.php index b0c4a65..2b6109a 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -6,12 +6,13 @@ namespace Alltube\Controller; -use Alltube\Exception\PasswordException; +use Alltube\Library\Exception\PasswordException; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Exception\WrongPasswordException; use Alltube\Locale; -use Alltube\Video; +use Exception; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; use Throwable; -use Exception; use Psr\Container\ContainerInterface; use Slim\Http\Request; use Slim\Http\Response; @@ -94,7 +95,7 @@ class FrontController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response - * @throws PasswordException + * @throws AlltubeLibraryException */ public function extractors(Request $request, Response $response) { @@ -103,7 +104,7 @@ class FrontController extends BaseController 'extractors.tpl', [ 'config' => $this->config, - 'extractors' => Video::getExtractors(), + '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 ' . @@ -141,7 +142,7 @@ class FrontController extends BaseController ] ); - return $response; + return $response->withStatus(403); } /** @@ -151,6 +152,7 @@ class FrontController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException */ private function getInfoResponse(Request $request, Response $response) { @@ -158,6 +160,8 @@ class FrontController extends BaseController $this->video->getJson(); } catch (PasswordException $e) { return $this->password($request, $response); + } catch (WrongPasswordException $e) { + return $this->displayError($request, $response, $this->localeManager->t('Wrong password')); } if (isset($this->video->entries)) { @@ -205,13 +209,14 @@ class FrontController extends BaseController * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException */ public function info(Request $request, Response $response) { $url = $request->getQueryParam('url') ?: $request->getQueryParam('v'); if (isset($url) && !empty($url)) { - $this->video = new Video($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. @@ -227,6 +232,33 @@ class FrontController extends BaseController } } + /** + * Display an user-friendly error. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * @param string $message Error message + * + * @return Response HTTP response + */ + protected function displayError(Request $request, Response $response, $message) + { + $this->view->render( + $response, + 'error.tpl', + [ + 'config' => $this->config, + 'error' => $message, + 'class' => 'video', + 'title' => $this->localeManager->t('Error'), + 'canonical' => $this->getCanonicalUrl($request), + 'locale' => $this->localeManager->getLocale(), + ] + ); + + return $response->withStatus(500); + } + /** * Display an error page. * @@ -241,7 +273,11 @@ class FrontController extends BaseController if ($this->config->debug) { $renderer = new HtmlErrorRenderer(true); $exception = $renderer->render($error); + $response->getBody()->write($exception->getAsString()); + foreach ($exception->getHeaders() as $header => $value) { + $response = $response->withHeader($header, $value); + } return $response->withStatus($exception->getStatusCode()); } else { @@ -251,20 +287,7 @@ class FrontController extends BaseController $message = ''; } - $this->view->render( - $response, - 'error.tpl', - [ - 'config' => $this->config, - 'error' => $message, - 'class' => 'video', - 'title' => $this->localeManager->t('Error'), - 'canonical' => $this->getCanonicalUrl($request), - 'locale' => $this->localeManager->getLocale(), - ] - ); - - return $response->withStatus(500); + return $this->displayError($request, $response, $message); } } diff --git a/controllers/JsonController.php b/controllers/JsonController.php index bfc9d75..9e37cc2 100644 --- a/controllers/JsonController.php +++ b/controllers/JsonController.php @@ -6,8 +6,7 @@ namespace Alltube\Controller; -use Alltube\Video; -use Exception; +use Alltube\Library\Exception\AlltubeLibraryException; use Slim\Http\Request; use Slim\Http\Response; @@ -19,24 +18,24 @@ class JsonController extends BaseController /** * Return the JSON object generated by youtube-dl. * - * @param Request $request PSR-7 request + * @param Request $request PSR-7 request * @param Response $response PSR-7 response * * @return Response HTTP response + * @throws AlltubeLibraryException */ public function json(Request $request, Response $response) { $url = $request->getQueryParam('url'); if (isset($url)) { - try { - $this->video = new Video($url, $this->getFormat($request), $this->getPassword($request)); + $this->video = $this->downloader->getVideo( + $url, + $this->getFormat($request), + $this->getPassword($request) + ); - return $response->withJson($this->video->getJson()); - } catch (Exception $e) { - return $response->withJson(['error' => $e->getMessage()]) - ->withStatus(500); - } + return $response->withJson($this->video->getJson()); } else { return $response->withJson(['error' => 'You need to provide the url parameter']) ->withStatus(400); diff --git a/i18n/template.pot b/i18n/template.pot index a7d5402..ce9d383 100644 --- a/i18n/template.pot +++ b/i18n/template.pot @@ -1,27 +1,6 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8\n" -#: templates/playlist.tpl:13 -msgid "Videos extracted from @title:" -msgstr "" - -#: templates/playlist.tpl:38 templates/password.tpl:11 templates/index.tpl:19 -#: templates/info.tpl:98 -msgid "Download" -msgstr "" - -#: templates/playlist.tpl:39 -msgid "More options" -msgstr "" - -#: templates/inc/header.tpl:4 -msgid "Switch language" -msgstr "" - -#: templates/inc/header.tpl:8 -msgid "Set language" -msgstr "" - #: templates/inc/footer.tpl:8 msgid "Code by @dev" msgstr "" @@ -46,6 +25,87 @@ msgstr "" 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 "" + +#: templates/info.tpl:29 +msgid "Available formats:" +msgstr "" + +#: templates/info.tpl:31 +msgid "Generic formats" +msgstr "" + +#: templates/info.tpl:36 +msgid "Detailed formats" +msgstr "" + +#: templates/info.tpl:80 +msgid "Stream the video through the server" +msgstr "" + +#: templates/info.tpl:86 +msgid "Convert into a custom format:" +msgstr "" + +#: templates/info.tpl:87 +msgid "Custom format" +msgstr "" + +#: templates/info.tpl:87 +msgid "Format to convert to" +msgstr "" + +#: templates/info.tpl:92 +msgid "with" +msgstr "" + +#: templates/info.tpl:93 +msgid "Bit rate" +msgstr "" + +#: templates/info.tpl:94 +msgid "Custom bitrate" +msgstr "" + +#: templates/info.tpl:97 +msgid "kbit/s audio" +msgstr "" + +#: templates/info.tpl:101 templates/playlist.tpl:38 templates/password.tpl:11 +#: templates/index.tpl:19 +msgid "Download" +msgstr "" + +#: templates/playlist.tpl:12 +msgid "Videos extracted from @title:" +msgstr "" + +#: templates/playlist.tpl:39 +msgid "More options" +msgstr "" + +#: templates/extractors.tpl:4 controllers/FrontController.php:109 +msgid "Supported websites" +msgstr "" + +#: templates/error.tpl:5 +msgid "An error occurred" +msgstr "" + +#: templates/error.tpl:6 +msgid "Please check the URL of your video." +msgstr "" + #: templates/password.tpl:5 msgid "This video is protected" msgstr "" @@ -70,144 +130,51 @@ msgstr "" msgid "From" msgstr "" -#: templates/index.tpl:29 +#: templates/index.tpl:31 msgid "to" msgstr "" -#: templates/index.tpl:36 +#: templates/index.tpl:39 msgid "See all supported websites" msgstr "" -#: templates/index.tpl:38 +#: templates/index.tpl:41 msgid "Drag this to your bookmarks bar:" msgstr "" -#: templates/index.tpl:39 +#: templates/index.tpl:43 msgid "Bookmarklet" msgstr "" -#: templates/info.tpl:13 -msgid "You are going to download @title." -msgstr "" - -#: templates/info.tpl:31 -msgid "Available formats:" -msgstr "" - -#: templates/info.tpl:33 -msgid "Generic formats" -msgstr "" - -#: templates/info.tpl:38 -msgid "Detailed formats" -msgstr "" - -#: templates/info.tpl:80 -msgid "Stream the video through the server" -msgstr "" - -#: templates/info.tpl:85 -msgid "Convert into a custom format:" -msgstr "" - -#: templates/info.tpl:86 -msgid "Custom format" -msgstr "" - -#: templates/info.tpl:86 -msgid "Format to convert to" -msgstr "" - -#: templates/info.tpl:91 -msgid "with" -msgstr "" - -#: templates/info.tpl:92 -msgid "Bit rate" -msgstr "" - -#: templates/info.tpl:93 -msgid "Custom bitrate" -msgstr "" - -#: templates/info.tpl:95 -msgid "kbit/s audio" -msgstr "" - -#: templates/error.tpl:5 -msgid "An error occurred" -msgstr "" - -#: templates/error.tpl:6 -msgid "Please check the URL of your video." -msgstr "" - -#: templates/extractors.tpl:4 controllers/FrontController.php:109 -msgid "Supported websites" -msgstr "" - -#: classes/Config.php:158 +#: classes/Config.php:156 msgid "Best" msgstr "" -#: classes/Config.php:159 +#: classes/Config.php:157 msgid "Remux best video with best audio" msgstr "" -#: classes/Config.php:160 +#: classes/Config.php:158 msgid "Worst" msgstr "" -#: classes/Video.php:159 +#: controllers/DownloadController.php:63 controllers/FrontController.php:164 msgid "Wrong password" msgstr "" -#: classes/Video.php:250 -msgid "youtube-dl returned an empty URL." -msgstr "" - -#: classes/Video.php:361 classes/Video.php:465 -msgid "Can't find avconv or ffmpeg at @path." -msgstr "" - -#: classes/Video.php:377 -msgid "Invalid start time: @from." -msgstr "" - -#: classes/Video.php:384 -msgid "Invalid end time: @to." -msgstr "" - -#: classes/Video.php:430 +#: controllers/DownloadController.php:68 msgid "Conversion of playlists is not supported." msgstr "" -#: classes/Video.php:435 classes/Video.php:578 +#: controllers/DownloadController.php:75 msgid "Conversion of M3U8 files is not supported." msgstr "" -#: classes/Video.php:437 +#: controllers/DownloadController.php:81 msgid "Conversion of DASH segments is not supported." msgstr "" -#: classes/Video.php:446 classes/Video.php:488 classes/Video.php:525 -#: classes/Video.php:558 classes/Video.php:586 -msgid "Could not open popen stream." -msgstr "" - -#: classes/Video.php:506 -msgid "This video does not have two URLs." -msgstr "" - -#: controllers/DownloadController.php:215 -msgid "You need to enable remux mode to merge two formats." -msgstr "" - -#: controllers/DownloadController.php:255 -msgid "Can't find URL of video." -msgstr "" - -#: controllers/FrontController.php:64 +#: controllers/FrontController.php:63 msgid "" "Easily download videos from Youtube, Dailymotion, Vimeo and other websites." msgstr "" @@ -227,18 +194,18 @@ msgid "" "You need a password in order to download this video with Alltube Download" msgstr "" -#: controllers/FrontController.php:169 +#: controllers/FrontController.php:172 msgid "Video download" msgstr "" -#: controllers/FrontController.php:171 +#: controllers/FrontController.php:174 msgid "Download video from @extractor" msgstr "" -#: controllers/FrontController.php:177 +#: controllers/FrontController.php:180 msgid "Download @title from @extractor" msgstr "" -#: controllers/FrontController.php:253 controllers/FrontController.php:284 +#: controllers/FrontController.php:253 msgid "Error" msgstr "" diff --git a/templates/error.tpl b/templates/error.tpl index 49663ec..98605b8 100644 --- a/templates/error.tpl +++ b/templates/error.tpl @@ -4,6 +4,6 @@ {include file="inc/logo.tpl"}

{t}An error occurred{/t}

{t}Please check the URL of your video.{/t} -

{$error|escape}

+

{$error|escape|nl2br}

{include file='inc/footer.tpl'} diff --git a/tests/BaseTest.php b/tests/BaseTest.php index d717b79..aac832b 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -7,7 +7,7 @@ namespace Alltube\Test; use Alltube\Config; -use Exception; +use Alltube\Exception\ConfigException; use PHPUnit\Framework\TestCase; /** @@ -33,7 +33,7 @@ abstract class BaseTest extends TestCase /** * Prepare tests. - * @throws Exception + * @throws ConfigException */ protected function setUp(): void { diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 09374cf..cbaf21d 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -7,7 +7,7 @@ namespace Alltube\Test; use Alltube\Config; -use Exception; +use Alltube\Exception\ConfigException; /** * Unit tests for the Config class. @@ -23,7 +23,7 @@ class ConfigTest extends BaseTest /** * Prepare tests. - * @throws Exception + * @throws ConfigException */ protected function setUp(): void { @@ -82,7 +82,7 @@ class ConfigTest extends BaseTest * Test the setFile function. * * @return void - * @throws Exception + * @throws ConfigException */ public function testSetFile() { @@ -97,7 +97,7 @@ class ConfigTest extends BaseTest */ public function testSetFileWithMissingFile() { - $this->expectException(Exception::class); + $this->expectException(ConfigException::class); Config::setFile('foo'); } @@ -105,7 +105,7 @@ class ConfigTest extends BaseTest * Test the setOptions function. * * @return void - * @throws Exception + * @throws ConfigException */ public function testSetOptions() { @@ -118,7 +118,7 @@ class ConfigTest extends BaseTest * Test the setOptions function. * * @return void - * @throws Exception + * @throws ConfigException */ public function testSetOptionsWithoutUpdate() { @@ -134,7 +134,7 @@ class ConfigTest extends BaseTest */ public function testSetOptionsWithBadYoutubedl() { - $this->expectException(Exception::class); + $this->expectException(ConfigException::class); Config::setOptions(['youtubedl' => 'foo']); } @@ -145,7 +145,7 @@ class ConfigTest extends BaseTest */ public function testSetOptionsWithBadPython() { - $this->expectException(Exception::class); + $this->expectException(ConfigException::class); Config::setOptions(['python' => 'foo']); } @@ -153,7 +153,7 @@ class ConfigTest extends BaseTest * Test the getInstance function with the CONVERT and PYTHON environment variables. * * @return void - * @throws Exception + * @throws ConfigException */ public function testGetInstanceWithEnv() { diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php index bd9f737..abb3756 100644 --- a/tests/ControllerTest.php +++ b/tests/ControllerTest.php @@ -9,13 +9,14 @@ namespace Alltube\Test; use Alltube\Controller\BaseController; use Alltube\Controller\DownloadController; use Alltube\Controller\FrontController; +use Alltube\Exception\ConfigException; use Alltube\LocaleManager; use Alltube\ViewFactory; -use Exception; use Slim\Container; use Slim\Http\Environment; use Slim\Http\Request; use Slim\Http\Response; +use SmartyException; /** * Abstract class used by the controller tests. @@ -51,7 +52,7 @@ abstract class ControllerTest extends BaseTest /** * Prepare tests. - * @throws Exception + * @throws ConfigException|SmartyException */ protected function setUp(): void { diff --git a/tests/ConvertedPlaylistArchiveStreamTest.php b/tests/ConvertedPlaylistArchiveStreamTest.php index e8cb002..0ed373f 100644 --- a/tests/ConvertedPlaylistArchiveStreamTest.php +++ b/tests/ConvertedPlaylistArchiveStreamTest.php @@ -6,9 +6,9 @@ namespace Alltube\Test; +use Alltube\Config; +use Alltube\Exception\ConfigException; use Alltube\Stream\ConvertedPlaylistArchiveStream; -use Alltube\Video; -use Exception; /** * Unit tests for the ConvertedPlaylistArchiveStream class. @@ -18,14 +18,16 @@ class ConvertedPlaylistArchiveStreamTest extends StreamTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException */ protected function setUp(): void { parent::setUp(); - $video = new Video('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); + $video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); - $this->stream = new ConvertedPlaylistArchiveStream($video); + $this->stream = new ConvertedPlaylistArchiveStream($downloader, $video); } } diff --git a/tests/DownloadControllerTest.php b/tests/DownloadControllerTest.php index 761ba21..019122b 100644 --- a/tests/DownloadControllerTest.php +++ b/tests/DownloadControllerTest.php @@ -8,7 +8,11 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\Controller\DownloadController; -use Exception; +use Alltube\Exception\ConfigException; +use Alltube\Library\Exception\EmptyUrlException; +use Alltube\Library\Exception\RemuxException; +use Alltube\Library\Exception\YoutubedlException; +use SmartyException; /** * Unit tests for the FrontController class. @@ -18,7 +22,7 @@ class DownloadControllerTest extends ControllerTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException|SmartyException */ protected function setUp(): void { @@ -64,7 +68,7 @@ class DownloadControllerTest extends ControllerTest * Test the download() function with streams enabled. * * @return void - * @throws Exception + * @throws ConfigException */ public function testDownloadWithStream() { @@ -80,7 +84,7 @@ class DownloadControllerTest extends ControllerTest * Test the download() function with an M3U stream. * * @return void - * @throws Exception + * @throws ConfigException */ public function testDownloadWithM3uStream() { @@ -100,7 +104,7 @@ class DownloadControllerTest extends ControllerTest * Test the download() function with an RTMP stream. * * @return void - * @throws Exception + * @throws ConfigException */ public function testDownloadWithRtmpStream() { @@ -118,7 +122,7 @@ class DownloadControllerTest extends ControllerTest * Test the download() function with a remuxed video. * * @return void - * @throws Exception + * @throws ConfigException */ public function testDownloadWithRemux() { @@ -140,7 +144,8 @@ class DownloadControllerTest extends ControllerTest */ public function testDownloadWithRemuxDisabled() { - $this->assertRequestIsServerError( + $this->expectException(RemuxException::class); + $this->getRequestResult( 'download', [ 'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU', @@ -166,7 +171,8 @@ class DownloadControllerTest extends ControllerTest */ public function testDownloadWithError() { - $this->assertRequestIsServerError('download', ['url' => 'http://example.com/foo']); + $this->expectException(YoutubedlException::class); + $this->getRequestResult('download', ['url' => 'http://example.com/foo']); } /** @@ -177,7 +183,8 @@ class DownloadControllerTest extends ControllerTest */ public function testDownloadWithEmptyUrl() { - $this->assertRequestIsServerError( + $this->expectException(EmptyUrlException::class); + $this->getRequestResult( 'download', ['url' => 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC'] ); @@ -188,7 +195,7 @@ class DownloadControllerTest extends ControllerTest * * @return void * @requires OS Linux - * @throws Exception + * @throws ConfigException */ public function testDownloadWithPlaylist() { @@ -204,7 +211,7 @@ class DownloadControllerTest extends ControllerTest * Test the download() function with an advanced conversion. * * @return void - * @throws Exception + * @throws ConfigException */ public function testDownloadWithAdvancedConversion() { diff --git a/tests/FrontControllerTest.php b/tests/FrontControllerTest.php index 92e4824..0e2ffe0 100644 --- a/tests/FrontControllerTest.php +++ b/tests/FrontControllerTest.php @@ -8,9 +8,12 @@ namespace Alltube\Test; use Alltube\Config; use Alltube\Controller\FrontController; +use Alltube\Exception\ConfigException; +use Alltube\Library\Exception\AlltubeLibraryException; use Exception; use Slim\Http\Environment; use Slim\Http\Request; +use SmartyException; /** * Unit tests for the FrontController class. @@ -25,7 +28,7 @@ class FrontControllerTest extends ControllerTest /** * Prepare tests. - * @throws Exception + * @throws ConfigException|SmartyException */ protected function setUp(): void { @@ -48,7 +51,7 @@ class FrontControllerTest extends ControllerTest * Test the constructor with streams enabled. * * @return void - * @throws Exception + * @throws ConfigException */ public function testConstructorWithStream() { @@ -99,7 +102,7 @@ class FrontControllerTest extends ControllerTest */ public function testPassword() { - $this->assertRequestIsOk('password'); + $this->assertRequestIsClientError('password'); } /** @@ -128,7 +131,7 @@ class FrontControllerTest extends ControllerTest * * @return void * @requires download - * @throws Exception + * @throws ConfigException */ public function testInfoWithAudio() { @@ -145,7 +148,7 @@ class FrontControllerTest extends ControllerTest * * @return void * @requires download - * @throws Exception + * @throws ConfigException */ public function testInfoWithVimeoAudio() { @@ -160,7 +163,7 @@ class FrontControllerTest extends ControllerTest * * @return void * @requires download - * @throws Exception + * @throws ConfigException */ public function testInfoWithUnconvertedAudio() { @@ -180,6 +183,7 @@ class FrontControllerTest extends ControllerTest * * @return void * @requires download + * @throws AlltubeLibraryException */ public function testInfoWithPassword() { @@ -199,8 +203,8 @@ class FrontControllerTest extends ControllerTest */ public function testInfoWithMissingPassword() { - $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962']); - $this->assertRequestIsOk('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); + $this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962']); + $this->assertRequestIsClientError('info', ['url' => 'http://vimeo.com/68375962', 'audio' => true]); } /** @@ -208,7 +212,7 @@ class FrontControllerTest extends ControllerTest * * @return void * @requires download - * @throws Exception + * @throws ConfigException */ public function testInfoWithStream() { diff --git a/tests/JsonControllerTest.php b/tests/JsonControllerTest.php index d2d6c3e..438b482 100644 --- a/tests/JsonControllerTest.php +++ b/tests/JsonControllerTest.php @@ -7,7 +7,9 @@ namespace Alltube\Test; use Alltube\Controller\JsonController; -use Exception; +use Alltube\Exception\ConfigException; +use Alltube\Library\Exception\YoutubedlException; +use SmartyException; /** * Unit tests for the FrontController class. @@ -16,7 +18,7 @@ class JsonControllerTest extends ControllerTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException|SmartyException */ protected function setUp(): void { @@ -44,7 +46,8 @@ class JsonControllerTest extends ControllerTest */ public function testJsonWithError() { - $this->assertRequestIsServerError('json', ['url' => 'http://example.com/foo']); + $this->expectException(YoutubedlException::class); + $this->getRequestResult('json', ['url' => 'http://example.com/foo']); } /** diff --git a/tests/PlaylistArchiveStreamTest.php b/tests/PlaylistArchiveStreamTest.php index f02a6ea..29b103f 100644 --- a/tests/PlaylistArchiveStreamTest.php +++ b/tests/PlaylistArchiveStreamTest.php @@ -6,9 +6,9 @@ namespace Alltube\Test; +use Alltube\Config; +use Alltube\Exception\ConfigException; use Alltube\Stream\PlaylistArchiveStream; -use Alltube\Video; -use Exception; /** * Unit tests for the PlaylistArchiveStream class. @@ -18,14 +18,16 @@ class PlaylistArchiveStreamTest extends StreamTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException */ protected function setUp(): void { parent::setUp(); - $video = new Video('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); + $video = $downloader->getVideo('https://www.youtube.com/playlist?list=PL1j4Ff8cAqPu5iowaeUAY8lRgkfT4RybJ'); - $this->stream = new PlaylistArchiveStream($video); + $this->stream = new PlaylistArchiveStream($downloader, $video); } } diff --git a/tests/VideoStubsTest.php b/tests/VideoStubsTest.php index 51f758d..789e839 100644 --- a/tests/VideoStubsTest.php +++ b/tests/VideoStubsTest.php @@ -6,36 +6,51 @@ namespace Alltube\Test; -use Alltube\Video; +use Alltube\Config; +use Alltube\Exception\ConfigException; +use Alltube\Library\Downloader; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Exception\PopenStreamException; +use Alltube\Library\Video; use Mockery; use phpmock\mockery\PHPMockery; -use Exception; /** * Unit tests for the Video class. * They are in a separate file so they can safely replace PHP functions with stubs. + * + * @requires download */ class VideoStubsTest extends BaseTest { /** - * Video URL used in many tests. + * Video used in many tests. * * @var Video */ private $video; + /** + * Downloader instance used in tests. + * + * @var Downloader + */ + private $downloader; + /** * Initialize properties used by test. - * @throws Exception + * @throws ConfigException */ protected function setUp(): void { parent::setUp(); - PHPMockery::mock('Alltube', 'popen'); - PHPMockery::mock('Alltube', 'fopen'); + PHPMockery::mock('Alltube\Library', 'popen'); + PHPMockery::mock('Alltube\Library', 'fopen'); - $this->video = new Video('https://www.youtube.com/watch?v=XJC9_JkzugE'); + $config = Config::getInstance(); + $this->downloader = $config->getDownloader(); + $this->video = $this->downloader->getVideo('https://www.youtube.com/watch?v=XJC9_JkzugE'); } /** @@ -52,55 +67,60 @@ class VideoStubsTest extends BaseTest * Test getAudioStream function with a buggy popen. * * @return void + * @throws AlltubeLibraryException */ public function testGetAudioStreamWithPopenError() { - $this->expectException(Exception::class); - $this->video->getAudioStream(); + $this->expectException(PopenStreamException::class); + $this->downloader->getAudioStream($this->video); } /** * Test getM3uStream function with a buggy popen. * * @return void + * @throws AlltubeLibraryException */ public function testGetM3uStreamWithPopenError() { - $this->expectException(Exception::class); - $this->video->getM3uStream(); + $this->expectException(PopenStreamException::class); + $this->downloader->getM3uStream($this->video); } /** * Test getRtmpStream function with a buggy popen. * * @return void + * @throws AlltubeLibraryException */ public function testGetRtmpStreamWithPopenError() { - $this->expectException(Exception::class); - $this->video->getRtmpStream(); + $this->expectException(PopenStreamException::class); + $this->downloader->getRtmpStream($this->video); } /** * Test getRemuxStream function with a buggy popen. * * @return void + * @throws AlltubeLibraryException */ public function testGetRemuxStreamWithPopenError() { - $this->expectException(Exception::class); + $this->expectException(PopenStreamException::class); $video = $this->video->withFormat('bestvideo+bestaudio'); - $video->getRemuxStream(); + $this->downloader->getRemuxStream($video); } /** * Test getConvertedStream function with a buggy popen. * * @return void + * @throws AlltubeLibraryException */ public function testGetConvertedStreamWithPopenError() { - $this->expectException(Exception::class); - $this->video->getConvertedStream(32, 'flv'); + $this->expectException(PopenStreamException::class); + $this->downloader->getConvertedStream($this->video, 32, 'flv'); } } diff --git a/tests/VideoTest.php b/tests/VideoTest.php index e6d4552..1b85038 100644 --- a/tests/VideoTest.php +++ b/tests/VideoTest.php @@ -7,26 +7,61 @@ namespace Alltube\Test; use Alltube\Config; -use Alltube\Exception\EmptyUrlException; -use Alltube\Exception\PasswordException; -use Alltube\Video; -use Exception; +use Alltube\Exception\ConfigException; +use Alltube\Library\Downloader; +use Alltube\Library\Exception\AlltubeLibraryException; +use Alltube\Library\Exception\AvconvException; +use Alltube\Library\Exception\InvalidProtocolConversionException; +use Alltube\Library\Exception\PasswordException; +use Alltube\Library\Exception\PlaylistConversionException; +use Alltube\Library\Exception\RemuxException; +use Alltube\Library\Exception\WrongPasswordException; +use Alltube\Library\Exception\YoutubedlException; +use Alltube\Library\Video; /** * Unit tests for the Video class. * @requires download + * @todo Split Downloader and Video tests. */ class VideoTest extends BaseTest { + /** + * Downloader instance used in tests. + * + * @var Downloader + */ + private $downloader; + + /** + * Video format used in tests. + * + * @var string + */ + private $format; + + /** + * Prepare tests. + * @throws ConfigException + */ + protected function setUp(): void + { + parent::setUp(); + + $config = Config::getInstance(); + $this->downloader = $config->getDownloader(); + $this->format = 'best'; + } + /** * Test getExtractors function. * * @return void - * @throws PasswordException + * @throws AlltubeLibraryException */ public function testGetExtractors() { - $this->assertContains('youtube', Video::getExtractors()); + $this->assertContains('youtube', $this->downloader->getExtractors()); } /** @@ -39,8 +74,7 @@ class VideoTest extends BaseTest * @param string $domain Domain * * @return void - * @throws PasswordException - * @throws EmptyUrlException + * @throws AlltubeLibraryException * @dataProvider urlProvider * @dataProvider m3uUrlProvider * @dataProvider remuxUrlProvider @@ -52,7 +86,7 @@ class VideoTest extends BaseTest /* @scrutinizer ignore-unused */ $extension, $domain ) { - $video = new Video($url, $format); + $video = new Video($this->downloader, $url, $format); foreach ($video->getUrl() as $videoURL) { $this->assertStringContainsString($domain, $videoURL); } @@ -62,12 +96,11 @@ class VideoTest extends BaseTest * Test getUrl function with a protected video. * * @return void - * @throws EmptyUrlException - * @throws PasswordException + * @throws AlltubeLibraryException */ public function testgetUrlWithPassword() { - $video = new Video('http://vimeo.com/68375962', 'best', 'youtube-dl'); + $video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'youtube-dl'); foreach ($video->getUrl() as $videoURL) { $this->assertStringContainsString('vimeocdn.com', $videoURL); } @@ -77,13 +110,12 @@ class VideoTest extends BaseTest * Test getUrl function with a protected video and no password. * * @return void - * @throws EmptyUrlException - * @throws PasswordException + * @throws AlltubeLibraryException */ public function testgetUrlWithMissingPassword() { - $this->expectException(Exception::class); - $video = new Video('http://vimeo.com/68375962'); + $this->expectException(PasswordException::class); + $video = new Video($this->downloader, 'http://vimeo.com/68375962', $this->format); $video->getUrl(); } @@ -91,13 +123,12 @@ class VideoTest extends BaseTest * Test getUrl function with a protected video and a wrong password. * * @return void - * @throws EmptyUrlException - * @throws PasswordException + * @throws AlltubeLibraryException */ public function testgetUrlWithWrongPassword() { - $this->expectException(Exception::class); - $video = new Video('http://vimeo.com/68375962', 'best', 'foo'); + $this->expectException(WrongPasswordException::class); + $video = new Video($this->downloader, 'http://vimeo.com/68375962', 'best', 'foo'); $video->getUrl(); } @@ -107,14 +138,13 @@ class VideoTest extends BaseTest * @param string $url URL * * @return void - * @throws EmptyUrlException - * @throws PasswordException - * @dataProvider ErrorUrlProvider + * @throws AlltubeLibraryException + * @dataProvider ErrorUrlProvider */ public function testgetUrlError($url) { - $this->expectException(Exception::class); - $video = new Video($url); + $this->expectException(YoutubedlException::class); + $video = new Video($this->downloader, $url, $this->format); $video->getUrl(); } @@ -224,13 +254,13 @@ class VideoTest extends BaseTest * @param string $format Format * * @return void + * @throws AlltubeLibraryException * @dataProvider urlProvider * @dataProvider m3uUrlProvider - * @throws PasswordException */ public function testGetJson($url, $format) { - $video = new Video($url, $format); + $video = new Video($this->downloader, $url, $format); $info = $video->getJson(); $this->assertObjectHasAttribute('webpage_url', $info); $this->assertObjectHasAttribute('url', $info); @@ -246,13 +276,13 @@ class VideoTest extends BaseTest * @param string $url URL * * @return void - * @dataProvider ErrorURLProvider - * @throws PasswordException + * @throws AlltubeLibraryException + * @dataProvider ErrorURLProvider */ public function testGetJsonError($url) { - $this->expectException(Exception::class); - $video = new Video($url); + $this->expectException(YoutubedlException::class); + $video = new Video($this->downloader, $url, $this->format); $video->getJson(); } @@ -265,14 +295,14 @@ class VideoTest extends BaseTest * @param string $extension File extension * * @return void + * @throws AlltubeLibraryException * @dataProvider urlProvider * @dataProvider m3uUrlProvider * @dataProvider remuxUrlProvider - * @throws PasswordException */ public function testGetFilename($url, $format, $filename, $extension) { - $video = new Video($url, $format); + $video = new Video($this->downloader, $url, $format); $this->assertEquals($video->getFilename(), $filename . '.' . $extension); } @@ -282,13 +312,13 @@ class VideoTest extends BaseTest * @param string $url URL * * @return void - * @dataProvider ErrorUrlProvider - * @throws PasswordException + * @throws AlltubeLibraryException + * @dataProvider ErrorUrlProvider */ public function testGetFilenameError($url) { - $this->expectException(Exception::class); - $video = new Video($url); + $this->expectException(YoutubedlException::class); + $video = new Video($this->downloader, $url, $this->format); $video->getFilename(); } @@ -300,73 +330,80 @@ class VideoTest extends BaseTest * * @return void * @dataProvider urlProvider - * @throws Exception + * @throws AlltubeLibraryException */ public function testGetAudioStream($url, $format) { - $video = new Video($url, $format); - $this->assertStream($video->getAudioStream()); + $video = new Video($this->downloader, $url, $format); + $this->assertStream($this->downloader->getAudioStream($video)); } /** * Test getAudioStream function without avconv. * - * @param string $url URL + * @param string $url URL * @param string $format Format * * @return void - * @dataProvider urlProvider + * @throws AlltubeLibraryException|ConfigException + * @dataProvider urlProvider */ public function testGetAudioStreamAvconvError($url, $format) { - $this->expectException(Exception::class); + $this->expectException(AvconvException::class); Config::setOptions(['avconv' => 'foobar']); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); - $video = new Video($url, $format); - $video->getAudioStream(); + $video = new Video($this->downloader, $url, $format, $this->format); + $downloader->getAudioStream($video); } /** * Test getAudioStream function with a M3U8 file. * - * @param string $url URL + * @param string $url URL * @param string $format Format * * @return void + * @throws AlltubeLibraryException * @dataProvider m3uUrlProvider */ public function testGetAudioStreamM3uError($url, $format) { - $this->expectException(Exception::class); - $video = new Video($url, $format); - $video->getAudioStream(); + $this->expectException(InvalidProtocolConversionException::class); + $video = new Video($this->downloader, $url, $format); + $this->downloader->getAudioStream($video); } /** * Test getAudioStream function with a DASH URL. * * @return void + * @throws AlltubeLibraryException */ public function testGetAudioStreamDashError() { - $this->expectException(Exception::class); - $video = new Video('https://vimeo.com/251997032', 'bestaudio/best'); - $video->getAudioStream(); + $this->expectException(InvalidProtocolConversionException::class); + $video = new Video($this->downloader, 'https://vimeo.com/251997032', 'bestaudio/best'); + $this->downloader->getAudioStream($video); } /** * Test getAudioStream function with a playlist. * * @return void + * @throws AlltubeLibraryException */ public function testGetAudioStreamPlaylistError() { - $this->expectException(Exception::class); + $this->expectException(PlaylistConversionException::class); $video = new Video( + $this->downloader, 'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC', 'best' ); - $video->getAudioStream(); + $this->downloader->getAudioStream($video); } /** @@ -390,12 +427,12 @@ class VideoTest extends BaseTest * * @return void * @dataProvider m3uUrlProvider - * @throws Exception + * @throws AlltubeLibraryException */ public function testGetM3uStream($url, $format) { - $video = new Video($url, $format); - $this->assertStream($video->getM3uStream()); + $video = new Video($this->downloader, $url, $format); + $this->assertStream($this->downloader->getM3uStream($video)); } /** @@ -406,28 +443,29 @@ class VideoTest extends BaseTest * * @return void * @dataProvider remuxUrlProvider - * @throws Exception + * @throws AlltubeLibraryException */ public function testGetRemuxStream($url, $format) { - $video = new Video($url, $format); - $this->assertStream($video->getRemuxStream()); + $video = new Video($this->downloader, $url, $format); + $this->assertStream($this->downloader->getRemuxStream($video)); } /** * Test getRemuxStream function with a video with only one URL. * - * @param string $url URL + * @param string $url URL * @param string $format Format * * @return void + * @throws AlltubeLibraryException * @dataProvider urlProvider */ public function testGetRemuxStreamWithWrongVideo($url, $format) { - $this->expectException(Exception::class); - $video = new Video($url, $format); - $video->getRemuxStream(); + $this->expectException(RemuxException::class); + $video = new Video($this->downloader, $url, $format); + $this->downloader->getRemuxStream($video); } /** @@ -437,34 +475,37 @@ class VideoTest extends BaseTest * @param string $format Format * * @return void + * @throws AlltubeLibraryException * @dataProvider rtmpUrlProvider - * @throws Exception */ public function testGetRtmpStream($url, $format) { $this->markTestIncomplete('We need to find another RTMP video.'); - $video = new Video($url, $format); + $video = new Video($this->downloader, $url, $format); - $this->assertStream($video->getRtmpStream()); + $this->assertStream($this->downloader->getRtmpStream($video)); } /** * Test getM3uStream function without avconv. * - * @param string $url URL + * @param string $url URL * @param string $format Format * * @return void + * @throws AlltubeLibraryException|ConfigException * @dataProvider m3uUrlProvider */ public function testGetM3uStreamAvconvError($url, $format) { - $this->expectException(Exception::class); + $this->expectException(AvconvException::class); Config::setOptions(['avconv' => 'foobar']); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); - $video = new Video($url, $format); - $video->getM3uStream(); + $video = new Video($downloader, $url, $format); + $downloader->getM3uStream($video); } /** @@ -475,27 +516,28 @@ class VideoTest extends BaseTest * * @return void * @dataProvider urlProvider - * @throws Exception + * @throws AlltubeLibraryException */ public function testGetConvertedStream($url, $format) { - $video = new Video($url, $format); - $this->assertStream($video->getConvertedStream(32, 'flv')); + $video = new Video($this->downloader, $url, $format); + $this->assertStream($this->downloader->getConvertedStream($video, 32, 'flv')); } /** * Test getConvertedStream function with a M3U8 file. * - * @param string $url URL + * @param string $url URL * @param string $format Format * * @return void + * @throws AlltubeLibraryException * @dataProvider m3uUrlProvider */ public function testGetConvertedStreamM3uError($url, $format) { - $this->expectException(Exception::class); - $video = new Video($url, $format); - $video->getConvertedStream(32, 'flv'); + $this->expectException(InvalidProtocolConversionException::class); + $video = new Video($this->downloader, $url, $format); + $this->downloader->getConvertedStream($video, 32, 'flv'); } } diff --git a/tests/YoutubeChunkStreamTest.php b/tests/YoutubeChunkStreamTest.php index 41a2f38..aec785d 100644 --- a/tests/YoutubeChunkStreamTest.php +++ b/tests/YoutubeChunkStreamTest.php @@ -6,9 +6,10 @@ namespace Alltube\Test; +use Alltube\Config; +use Alltube\Exception\ConfigException; +use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Stream\YoutubeChunkStream; -use Alltube\Video; -use Exception; /** * Unit tests for the YoutubeChunkStream class. @@ -18,14 +19,17 @@ class YoutubeChunkStreamTest extends StreamTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException + * @throws AlltubeLibraryException */ protected function setUp(): void { parent::setUp(); - $video = new Video('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); + $video = $downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); - $this->stream = new YoutubeChunkStream($video->getHttpResponse()); + $this->stream = new YoutubeChunkStream($downloader->getHttpResponse($video)); } } diff --git a/tests/YoutubeStreamTest.php b/tests/YoutubeStreamTest.php index b8b4d13..d551e0c 100644 --- a/tests/YoutubeStreamTest.php +++ b/tests/YoutubeStreamTest.php @@ -6,9 +6,10 @@ namespace Alltube\Test; +use Alltube\Config; +use Alltube\Exception\ConfigException; +use Alltube\Library\Exception\AlltubeLibraryException; use Alltube\Stream\YoutubeStream; -use Alltube\Video; -use Exception; /** * Unit tests for the YoutubeStream class. @@ -18,15 +19,17 @@ class YoutubeStreamTest extends StreamTest { /** * Prepare tests. - * @throws Exception + * @throws ConfigException|AlltubeLibraryException */ protected function setUp(): void { parent::setUp(); - $video = new Video('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '135'); + $config = Config::getInstance(); + $downloader = $config->getDownloader(); + $video = $downloader->getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '135'); - $this->stream = new YoutubeStream($video); + $this->stream = new YoutubeStream($downloader, $video); } /** From d4b3a82ebbf33f556e1b2bf51fee431145bcbccd Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Sun, 21 Jun 2020 15:04:01 +0200 Subject: [PATCH 2/2] Remove obsolete Composer config --- composer.json | 7 ------- composer.lock | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 83f059b..a5122bb 100644 --- a/composer.json +++ b/composer.json @@ -34,13 +34,6 @@ "symfony/error-handler": "^5.0", "symfony/var-dumper": "^5.0" }, - "extra": { - "paas": { - "nginx-includes": [ - "resources/nginx.conf" - ] - } - }, "repositories": [ { "type": "composer", diff --git a/composer.lock b/composer.lock index d164fd2..b612219 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dba22d2bff872c8e1310567d4336db16", + "content-hash": "03b37b613f8ae3881395adf1cbc72b67", "packages": [ { "name": "anam/phantomjs-linux-x86-binary",