Cleanup playlists
This commit is contained in:
parent
3f053d9eed
commit
43cbd4f6fe
10 changed files with 175 additions and 121 deletions
|
@ -38,7 +38,7 @@ class Config
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $params = ['--no-playlist', '--no-warnings', '--playlist-end', 1];
|
public $params = ['--no-warnings'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable audio conversion.
|
* Enable audio conversion.
|
||||||
|
|
|
@ -113,14 +113,7 @@ class VideoDownload
|
||||||
* */
|
* */
|
||||||
public function getJSON($url, $format = null, $password = null)
|
public function getJSON($url, $format = null, $password = null)
|
||||||
{
|
{
|
||||||
$jsonArray = preg_split( "/\r|\n/", $this->getProp($url, $format, 'dump-json', $password), -1,
|
return json_decode($this->getProp($url, $format, 'dump-single-json', $password));
|
||||||
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
|
||||||
$decodedJson = array();
|
|
||||||
foreach ($jsonArray as $oneJson)
|
|
||||||
{
|
|
||||||
array_push($decodedJson, json_decode($oneJson));
|
|
||||||
}
|
|
||||||
return $decodedJson;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -213,7 +213,7 @@ class FrontController
|
||||||
private function getVideoResponse(Request $request, Response $response, array $params, $password = null)
|
private function getVideoResponse(Request $request, Response $response, array $params, $password = null)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$vidarr = $this->download->getJSON($params['url'], $this->defaultFormat, $password);
|
$video = $this->download->getJSON($params['url'], $this->defaultFormat, $password);
|
||||||
} catch (PasswordException $e) {
|
} catch (PasswordException $e) {
|
||||||
return $this->password($request, $response);
|
return $this->password($request, $response);
|
||||||
}
|
}
|
||||||
|
@ -222,14 +222,19 @@ class FrontController
|
||||||
} else {
|
} else {
|
||||||
$protocol = '[protocol^=http]';
|
$protocol = '[protocol^=http]';
|
||||||
}
|
}
|
||||||
|
if (isset($video->entries)) {
|
||||||
|
$template = 'playlist.tpl';
|
||||||
|
} else {
|
||||||
|
$template = 'video.tpl';
|
||||||
|
}
|
||||||
$this->view->render(
|
$this->view->render(
|
||||||
$response,
|
$response,
|
||||||
'video.tpl',
|
$template,
|
||||||
[
|
[
|
||||||
'vidarr' => $vidarr,
|
'video' => $video,
|
||||||
'class' => 'video',
|
'class' => 'video',
|
||||||
'title' => $vidarr[0]->title,
|
'title' => $video->title,
|
||||||
'description' => 'Download "'.$vidarr[0]->title.'" from '.$vidarr[0]->extractor_key,
|
'description' => 'Download "'.$video->title.'" from '.$video->extractor_key,
|
||||||
'protocol' => $protocol,
|
'protocol' => $protocol,
|
||||||
'config' => $this->config,
|
'config' => $this->config,
|
||||||
'canonical' => $this->getCanonicalUrl($request),
|
'canonical' => $this->getCanonicalUrl($request),
|
||||||
|
@ -327,7 +332,11 @@ class FrontController
|
||||||
$response = $response->withBody($stream->getBody());
|
$response = $response->withBody($stream->getBody());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"');
|
$response = $response->withHeader(
|
||||||
|
'Content-Disposition',
|
||||||
|
'attachment; filename="'.
|
||||||
|
$this->download->getFilename($url, $format, $password).'"'
|
||||||
|
);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -397,10 +397,34 @@ padding:3px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Playlists */
|
||||||
|
.playlist-entry .thumb {
|
||||||
|
float: left;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-entry {
|
||||||
|
clear: both;
|
||||||
|
padding-top: 2em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-entry h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-entry h3 a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-entry h3 a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-entry .downloadBtn {
|
||||||
|
font-size: 16px;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__.'/vendor/autoload.php';
|
require_once __DIR__.'/vendor/autoload.php';
|
||||||
use Alltube\Config;
|
use Alltube\Config;
|
||||||
use Alltube\Controller\FrontController;
|
use Alltube\Controller\FrontController;
|
||||||
|
|
34
js/cast.js
34
js/cast.js
|
@ -2,7 +2,7 @@
|
||||||
/*jslint browser: true, nomen: true */
|
/*jslint browser: true, nomen: true */
|
||||||
var castModule = (function () {
|
var castModule = (function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
var launchBtn, disabledBtn, stopBtn, session, videoLink;
|
var launchBtn, disabledBtn, stopBtn, session;
|
||||||
|
|
||||||
function receiverListener(e) {
|
function receiverListener(e) {
|
||||||
return (e === chrome.cast.ReceiverAvailability.AVAILABLE);
|
return (e === chrome.cast.ReceiverAvailability.AVAILABLE);
|
||||||
|
@ -51,7 +51,7 @@ var castModule = (function () {
|
||||||
|
|
||||||
function onRequestSessionSuccess(e) {
|
function onRequestSessionSuccess(e) {
|
||||||
session = e;
|
session = e;
|
||||||
var videoURL = videoLink.dataset.video, mediaInfo = new chrome.cast.media.MediaInfo(videoURL, 'video/' + videoLink.dataset.ext), request = new chrome.cast.media.LoadRequest(mediaInfo);
|
var videoLink = document.getElementById('video_link'), videoURL = videoLink.dataset.video, mediaInfo = new chrome.cast.media.MediaInfo(videoURL, 'video/' + videoLink.dataset.ext), request = new chrome.cast.media.LoadRequest(mediaInfo);
|
||||||
session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError);
|
session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,23 +59,19 @@ var castModule = (function () {
|
||||||
throw e.description;
|
throw e.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
function launchCast(event) {
|
function launchCast() {
|
||||||
videoLink = event.target || event.srcElement;
|
|
||||||
chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError);
|
chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInitSuccess() {
|
function onInitSuccess() {
|
||||||
launchBtn = document.getElementsByClassName('cast_btn_launch');
|
launchBtn = document.getElementById('cast_btn_launch');
|
||||||
disabledBtn = document.getElementsByClassName('cast_disabled');
|
disabledBtn = document.getElementById('cast_disabled');
|
||||||
stopBtn = document.getElementsByClassName('cast_btn_stop');
|
stopBtn = document.getElementById('cast_btn_stop');
|
||||||
if (launchBtn.length > 0) {
|
if (launchBtn) {
|
||||||
var i;
|
disabledBtn.classList.add('cast_hidden');
|
||||||
for (i = 0; i < launchBtn.length; i++) {
|
launchBtn.classList.remove('cast_hidden');
|
||||||
disabledBtn[i].classList.add('cast_hidden');
|
launchBtn.addEventListener('click', launchCast, false);
|
||||||
launchBtn[i].classList.remove('cast_hidden');
|
stopBtn.addEventListener('click', stopCast, false);
|
||||||
launchBtn[i].addEventListener('click', launchCast, false);
|
|
||||||
stopBtn[i].addEventListener('click', stopCast, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +94,11 @@ var castModule = (function () {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: function () {
|
init: function () {
|
||||||
var intro = document.getElementsByClassName('download_intro'), i;
|
var intro = document.getElementById('download_intro');
|
||||||
for (i = 0; i < intro.length; i++) {
|
if (intro) {
|
||||||
intro[i].insertAdjacentHTML('beforeend', '<img class="cast_disabled cast_icon" id="cast_disabled'+i+'" src="img/ic_media_route_disabled_holo_light.png" alt="" title="Google Cast is not supported on this browser." /> <img class="cast_btn_launch cast_btn cast_hidden cast_icon" id="cast_btn_launch'+i+'" src="img/ic_media_route_off_holo_light.png" title="Cast to ChromeCast" alt="Google Cast™" /> <img src="img/ic_media_route_on_holo_light.png" alt="Casting to ChromeCast…" title="Stop casting" id="cast_btn_stop'+i+'" class="cast_btn_stop cast_btn cast_hidden cast_icon" />');
|
intro.insertAdjacentHTML('beforeend', '<img class="cast_icon" id="cast_disabled" src="img/ic_media_route_disabled_holo_light.png" alt="" title="Google Cast is not supported on this browser." /> <img class="cast_btn cast_hidden cast_icon" id="cast_btn_launch" src="img/ic_media_route_off_holo_light.png" title="Cast to ChromeCast" alt="Google Cast™" /> <img src="img/ic_media_route_on_holo_light.png" alt="Casting to ChromeCast…" title="Stop casting" id="cast_btn_stop" class="cast_btn cast_hidden cast_icon" />');
|
||||||
|
window.__onGCastApiAvailable = loadCastApi;
|
||||||
}
|
}
|
||||||
window.__onGCastApiAvailable = loadCastApi;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
19
templates/playlist.tpl
Normal file
19
templates/playlist.tpl
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{include file="inc/head.tpl"}
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="main">
|
||||||
|
{include file="inc/logo.tpl"}
|
||||||
|
<p>Videos extracted from the<i>
|
||||||
|
<a href="{$video->webpage_url}">
|
||||||
|
{$video->title}</a></i> playlist:
|
||||||
|
</p>
|
||||||
|
{foreach $video->entries as $video}
|
||||||
|
<div class="playlist-entry">
|
||||||
|
<img class="thumb" src="{$video->thumbnail}" alt="" width="200" />
|
||||||
|
<h3><a href="{$video->webpage_url}">{$video->title}</a></h3>
|
||||||
|
<a class="downloadBtn" href="{path_for name="redirect"}?url={$video->webpage_url}">Download</a>
|
||||||
|
</div>
|
||||||
|
{/foreach}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{include file="inc/footer.tpl"}
|
|
@ -3,95 +3,92 @@
|
||||||
<div itemscope itemtype="http://schema.org/VideoObject">
|
<div itemscope itemtype="http://schema.org/VideoObject">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
{include file="inc/logo.tpl"}
|
{include file="inc/logo.tpl"}
|
||||||
{foreach $vidarr as $video}
|
<p id="download_intro">You are going to download<i itemprop="name">
|
||||||
<p id="download_intro{$video@index}" class="download_intro">You are going to download<i itemprop="name">
|
<a itemprop="url" id="video_link"
|
||||||
<a itemprop="url" id="video_link{$video@index}"
|
data-ext="{$video->ext}"
|
||||||
class="video_link"
|
data-video="{$video->url|escape}"
|
||||||
data-ext="{$video->ext}"
|
href="{$video->webpage_url}">
|
||||||
data-video="{$video->url|escape}"
|
{$video->title}</a></i>.
|
||||||
href="{$video->webpage_url}">
|
</p>
|
||||||
{$video->title}</a></i>.
|
{if isset($video->thumbnail)}
|
||||||
</p>
|
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt="" />
|
||||||
{if isset($video->thumbnail)}
|
{/if}
|
||||||
<img itemprop="thumbnailUrl" class="thumb" src="{$video->thumbnail}" alt="" />
|
{if isset($video->description)}
|
||||||
{/if}
|
<meta itemprop="description" content="{$video->description|escape}" />
|
||||||
{if isset($video->description)}
|
{/if}
|
||||||
<meta itemprop="description" content="{$video->description|escape}" />
|
{if isset($video->upload_date)}
|
||||||
{/if}
|
<meta itemprop="uploadDate" content="{$video->upload_date}" />
|
||||||
{if isset($video->upload_date)}
|
{/if}
|
||||||
<meta itemprop="uploadDate" content="{$video->upload_date}" />
|
<br/>
|
||||||
{/if}
|
{if isset($video->formats)}
|
||||||
<br/>
|
<h3><label for="format">Available formats:</label></h3>
|
||||||
{if isset($video->formats)}
|
<form action="{path_for name="redirect"}">
|
||||||
<h3><label for="format{$video@index}">Available formats:</label></h3>
|
<input type="hidden" name="url" value="{$video->webpage_url}" />
|
||||||
<form action="{path_for name="redirect"}">
|
{if $uglyUrls}
|
||||||
<input type="hidden" name="url" value="{$video->webpage_url}" />
|
<input type="hidden" name="page" value="redirect" />
|
||||||
{if uglyUrls}
|
{/if}
|
||||||
<input type="hidden" name="page" value="redirect" />
|
<select name="format" id="format" class="formats monospace">
|
||||||
{/if}
|
<optgroup label="Generic formats">
|
||||||
<select name="format" id="format{$video@index}" class="formats monospace">
|
<option value="best{$protocol}">
|
||||||
<optgroup label="Generic formats">
|
{strip}
|
||||||
<option value="best{$protocol}">
|
Best ({$video->ext})
|
||||||
|
{/strip}
|
||||||
|
</option>
|
||||||
|
{if $remux}
|
||||||
|
<option value="bestvideo+bestaudio">
|
||||||
|
Remux best video with best audio
|
||||||
|
</option>
|
||||||
|
{/if}
|
||||||
|
<option value="worst{$protocol}">
|
||||||
|
Worst
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Detailed formats" class="monospace">
|
||||||
|
{foreach $video->formats as $format}
|
||||||
|
{if $config->stream || $format->protocol|in_array:array('http', 'https')}
|
||||||
{strip}
|
{strip}
|
||||||
Best ({$video->ext})
|
<option value="{$format->format_id}">
|
||||||
{/strip}
|
{$format->ext}
|
||||||
</option>
|
{for $foo=1 to (5 - ($format->ext|strlen))}
|
||||||
{if $remux}
|
|
||||||
<option value="bestvideo+bestaudio">
|
{/for}
|
||||||
Remux best video with best audio
|
{if isset($format->width)}
|
||||||
</option>
|
{$format->width}x{$format->height}
|
||||||
{/if}
|
{for $foo=1 to (10 - (("{$format->width}x{$format->height}")|strlen))}
|
||||||
<option value="worst{$protocol}">
|
|
||||||
Worst
|
|
||||||
</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup label="Detailed formats" class="monospace">
|
|
||||||
{foreach $video->formats as $format}
|
|
||||||
{if $config->stream || $format->protocol|in_array:array('http', 'https')}
|
|
||||||
{strip}
|
|
||||||
<option value="{$format->format_id}">
|
|
||||||
{$format->ext}
|
|
||||||
{for $foo=1 to (5 - ($format->ext|strlen))}
|
|
||||||
|
|
||||||
{/for}
|
{/for}
|
||||||
{if isset($format->width)}
|
{else}
|
||||||
{$format->width}x{$format->height}
|
{for $foo=1 to 10}
|
||||||
{for $foo=1 to (10 - (("{$format->width}x{$format->height}")|strlen))}
|
|
||||||
|
{/for}
|
||||||
{/for}
|
{/if}
|
||||||
{else}
|
{if isset($format->filesize)}
|
||||||
{for $foo=1 to 10}
|
{($format->filesize/1000000)|round:2} MB
|
||||||
|
{for $foo=1 to (7 - (($format->filesize/1000000)|round:2|strlen))}
|
||||||
{/for}
|
|
||||||
{/if}
|
{/for}
|
||||||
{if isset($format->filesize)}
|
{else}
|
||||||
{($format->filesize/1000000)|round:2} MB
|
{for $foo=1 to 10}
|
||||||
{for $foo=1 to (7 - (($format->filesize/1000000)|round:2|strlen))}
|
|
||||||
|
{/for}
|
||||||
{/for}
|
{/if}
|
||||||
{else}
|
{if isset($format->format_note)}
|
||||||
{for $foo=1 to 10}
|
{$format->format_note}
|
||||||
|
{/if}
|
||||||
{/for}
|
({$format->format_id})
|
||||||
{/if}
|
</option>
|
||||||
{if isset($format->format_note)}
|
{/strip}
|
||||||
{$format->format_note}
|
{/if}
|
||||||
{/if}
|
{/foreach}
|
||||||
({$format->format_id})
|
</optgroup>
|
||||||
</option>
|
</select><br/><br/>
|
||||||
{/strip}
|
<input class="downloadBtn" type="submit" value="Download" /><br/>
|
||||||
{/if}
|
</form>
|
||||||
{/foreach}
|
{else}
|
||||||
</optgroup>
|
<input type="hidden" name="format" value="best{$protocol}" />
|
||||||
</select><br/><br/>
|
<a class="downloadBtn"
|
||||||
<input class="downloadBtn" type="submit" value="Download" /><br/>
|
href="{$video->url|escape}">Download</a><br/>
|
||||||
</form>
|
{/if}
|
||||||
{else}
|
|
||||||
<input type="hidden" name="format" value="best{$protocol}" />
|
|
||||||
<a class="downloadBtn" href="{$video->url|escape}">Download</a><br/>
|
|
||||||
{/if}
|
|
||||||
<hr />
|
|
||||||
{/foreach}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{include file="inc/footer.tpl"}
|
{include file="inc/footer.tpl"}
|
||||||
|
|
|
@ -264,6 +264,22 @@ class FrontControllerTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($result->isOk());
|
$this->assertTrue($result->isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the video() function with a playlist.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testVideoWithPlaylist()
|
||||||
|
{
|
||||||
|
$result = $this->controller->video(
|
||||||
|
$this->request->withQueryParams(
|
||||||
|
['url'=>'https://www.youtube.com/playlist?list=PLgdySZU6KUXL_8Jq5aUkyNV7wCa-4wZsC']
|
||||||
|
),
|
||||||
|
$this->response
|
||||||
|
);
|
||||||
|
$this->assertTrue($result->isOk());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the error() function.
|
* Test the error() function.
|
||||||
*
|
*
|
||||||
|
|
|
@ -266,7 +266,6 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertObjectHasAttribute('title', $info);
|
$this->assertObjectHasAttribute('title', $info);
|
||||||
$this->assertObjectHasAttribute('extractor_key', $info);
|
$this->assertObjectHasAttribute('extractor_key', $info);
|
||||||
$this->assertObjectHasAttribute('formats', $info);
|
$this->assertObjectHasAttribute('formats', $info);
|
||||||
$this->assertObjectHasAttribute('_filename', $info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue