Add support for password protected videos

This commit is contained in:
Pierre Rudloff 2016-10-20 23:01:31 +02:00
parent 621ccfb491
commit e34b01f2c4
9 changed files with 196 additions and 29 deletions

View File

@ -0,0 +1,13 @@
<?php
/**
* PasswordException class
*/
namespace Alltube;
/**
* Exception thrown when a video requires a password
*/
class PasswordException extends \Exception
{
}

View File

@ -71,13 +71,14 @@ class VideoDownload
/**
* Get a property from youtube-dl.
*
* @param string $url URL to parse
* @param string $format Format
* @param string $prop Property
* @param string $url URL to parse
* @param string $format Format
* @param string $prop Property
* @param string $password Video password
*
* @return string
*/
private function getProp($url, $format = null, $prop = 'dump-json')
private function getProp($url, $format = null, $prop = 'dump-json', $password = null)
{
$this->procBuilder->setArguments(
[
@ -88,10 +89,21 @@ class VideoDownload
if (isset($format)) {
$this->procBuilder->add('-f '.$format);
}
if (isset($password)) {
$this->procBuilder->add('--video-password');
$this->procBuilder->add($password);
}
$process = $this->procBuilder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new \Exception($process->getErrorOutput());
$errorOutput = trim($process->getErrorOutput());
if ($errorOutput == 'ERROR: This video is protected by a password, use the --video-password option') {
throw new PasswordException($errorOutput);
} elseif (substr($errorOutput, 0, 21) == 'ERROR: Wrong password') {
throw new \Exception('Wrong password');
} else {
throw new \Exception($errorOutput);
}
} else {
return $process->getOutput();
}
@ -100,40 +112,43 @@ class VideoDownload
/**
* Get all information about a video.
*
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $password Video password
*
* @return object Decoded JSON
* */
public function getJSON($url, $format = null)
public function getJSON($url, $format = null, $password = null)
{
return json_decode($this->getProp($url, $format, 'dump-json'));
return json_decode($this->getProp($url, $format, 'dump-json', $password));
}
/**
* Get URL of video from URL of page.
*
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $password Video password
*
* @return string URL of video
* */
public function getURL($url, $format = null)
public function getURL($url, $format = null, $password = null)
{
return $this->getProp($url, $format, 'get-url');
return $this->getProp($url, $format, 'get-url', $password);
}
/**
* Get filename of video file from URL of page.
*
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $url URL of page
* @param string $format Format to use for the video
* @param string $password Video password
*
* @return string Filename of extracted video
* */
public function getFilename($url, $format = null)
public function getFilename($url, $format = null, $password = null)
{
return trim($this->getProp($url, $format, 'get-filename'));
return trim($this->getProp($url, $format, 'get-filename', $password));
}
/**

View File

@ -11,7 +11,8 @@
"symfony/yaml": "~3.1.0",
"symfony/process": "~3.1.0",
"ptachoire/process-builder-chain": "~1.2.0",
"rudloff/smarty-plugin-noscheme": "~0.1.0"
"rudloff/smarty-plugin-noscheme": "~0.1.0",
"aura/session": "~2.1.0"
},
"require-dev": {
"symfony/var-dumper": "~3.1.0",

66
composer.lock generated
View File

@ -4,9 +4,71 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "2814570fa83cedc8e079c1d236a787d2",
"content-hash": "91057608d6f29b8de8a9761bb419f19c",
"hash": "7feb22c9a83e389562253bd1e7389080",
"content-hash": "0ca3a07c96a159c3a44ae007b56a9fbf",
"packages": [
{
"name": "aura/session",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/auraphp/Aura.Session.git",
"reference": "7d2f7d41ad693970b5b6b83facca0961d3378883"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/auraphp/Aura.Session/zipball/7d2f7d41ad693970b5b6b83facca0961d3378883",
"reference": "7d2f7d41ad693970b5b6b83facca0961d3378883",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"aura/di": "~2.0"
},
"suggest": {
"ext-mcrypt": "Mcrypt generates the next best secure CSRF tokens.",
"ext-openssl": "OpenSSL generates the best secure CSRF tokens.",
"ircmaxell/random-lib": "A Library For Generating Secure Random Numbers",
"paragonie/random_compat": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7"
},
"type": "library",
"extra": {
"aura": {
"type": "library",
"config": {
"common": "Aura\\Session\\_Config\\Common"
}
}
},
"autoload": {
"psr-4": {
"Aura\\Session\\": "src/",
"Aura\\Session\\_Config\\": "config/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Aura.Session Contributors",
"homepage": "https://github.com/auraphp/Aura.Session/contributors"
}
],
"description": "Provides session management functionality, including lazy session starting, session segments, next-request-only (\"flash\") values, and CSRF tools.",
"homepage": "https://github.com/auraphp/Aura.Session",
"keywords": [
"csrf",
"flash",
"flash message",
"session",
"sessions"
],
"time": "2016-10-03 20:28:32"
},
{
"name": "container-interop/container-interop",
"version": "1.1.0",

View File

@ -6,6 +6,7 @@ namespace Alltube\Controller;
use Alltube\Config;
use Alltube\VideoDownload;
use Alltube\PasswordException;
use Interop\Container\ContainerInterface;
use Slim\Container;
use Slim\Http\Request;
@ -48,6 +49,9 @@ class FrontController
$this->config = Config::getInstance();
$this->download = new VideoDownload();
$this->container = $container;
$session_factory = new \Aura\Session\SessionFactory;
$session = $session_factory->newInstance($_COOKIE);
$this->sessionSegment = $session->getSegment('Alltube\Controller\FrontController');
}
/**
@ -98,6 +102,28 @@ class FrontController
}
}
/**
* Display a password prompt
* @param Request $request PSR-7 request
* @param Response $response PSR-7 response
*
* @return Response HTTP response
*/
public function password(Request $request, Response $response)
{
if ($this->container instanceof Container) {
$this->container->view->render(
$response,
'password.tpl',
[
'class' => 'password',
'title' => 'Password prompt',
'description' => 'You need a password in order to download this video with Alltube Download',
]
);
}
}
/**
* Dislay information about the video.
*
@ -109,8 +135,11 @@ class FrontController
public function video(Request $request, Response $response)
{
$params = $request->getQueryParams();
$this->config = Config::getInstance();
if (isset($params['url'])) {
$password = $request->getParam('password');
if (isset($password)) {
$this->sessionSegment->setFlash($params['url'], $password);
}
if (isset($params['audio'])) {
try {
$url = $this->download->getURL($params['url'], 'mp3[protocol^=http]');
@ -132,7 +161,11 @@ class FrontController
return $response;
}
} else {
$video = $this->download->getJSON($params['url']);
try {
$video = $this->download->getJSON($params['url'], null, $password);
} catch (PasswordException $e) {
return $this->password($request, $response);
}
if ($this->container instanceof Container) {
$this->container->view->render(
$response,
@ -190,9 +223,11 @@ class FrontController
$params = $request->getQueryParams();
if (isset($params['url'])) {
try {
$url = $this->download->getURL($params['url'], $params['format']);
$url = $this->download->getURL($params['url'], $request->getParam('format'), $this->sessionSegment->getFlash($params['url']));
return $response->withRedirect($url);
} catch (PasswordException $e) {
return $response->withRedirect($this->container->get('router')->pathFor('video').'?url='.urlencode($params['url']));
} catch (\Exception $e) {
$response->getBody()->write($e->getMessage());

View File

@ -35,7 +35,7 @@ $app->get(
'/extractors',
[$controller, 'extractors']
)->setName('extractors');
$app->get(
$app->any(
'/video',
[$controller, 'video']
)->setName('video');

13
templates/password.tpl Normal file
View File

@ -0,0 +1,13 @@
{include file='inc/head.tpl'}
<div class="wrapper">
<div class="main">
{include file="inc/logo.tpl"}
<h2>This video is protected</h2>
<p>You need a password in order to download this video.</p>
<form action="" method="POST">
<input class="URLinput" type="password" name="password" title="Video password" />
<br/><br/>
<input class="downloadBtn" type="submit" value="Download" />
</form>
</div>
{include file='inc/footer.tpl'}

View File

@ -28,11 +28,7 @@
<optgroup label="Generic formats">
<option value="best[protocol^=http]">
{strip}
Best ({$video->ext}
{if isset($video->filesize)}
{$video->filesize}
{/if}
)
Best ({$video->ext})
{/strip}
</option>
<option value="worst[protocol^=http]">

View File

@ -89,6 +89,38 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
$this->assertContains($domain, $videoURL);
}
/**
* Test getURL function with a protected video
*
* @return void
*/
public function testGetURLWithPassword()
{
$this->assertContains('vimeocdn.com', $this->download->getURL('http://vimeo.com/68375962', null, 'youtube-dl'));
}
/**
* Test getURL function with a protected video and no password.
*
* @return void
* @expectedException \Alltube\PasswordException
*/
public function testGetURLWithMissingPassword()
{
$this->download->getURL('http://vimeo.com/68375962');
}
/**
* Test getURL function with a protected video and a wrong password.
*
* @return void
* @expectedException Exception
*/
public function testGetURLWithWrongPassword()
{
$this->download->getURL('http://vimeo.com/68375962', null, 'foo');
}
/**
* Test getURL function errors.
*