Experimental support for M3U streams
This commit is contained in:
parent
1a6e995262
commit
28a8addbf1
5 changed files with 56 additions and 14 deletions
|
@ -38,7 +38,12 @@ class Config
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $params = ['--no-playlist', '--no-warnings', '-f best[protocol^=http]', '--playlist-end', 1];
|
public $params = [
|
||||||
|
'--no-playlist', '--no-warnings',
|
||||||
|
//We can allow non-HTTP URLs on the feature/stream branch
|
||||||
|
'-f best',
|
||||||
|
'--playlist-end', 1
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable audio conversion.
|
* Enable audio conversion.
|
||||||
|
|
|
@ -292,4 +292,28 @@ class VideoDownload
|
||||||
|
|
||||||
return popen($chain->getProcess()->getCommandLine(), 'r');
|
return popen($chain->getProcess()->getCommandLine(), 'r');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getM3uStream(\stdClass $video)
|
||||||
|
{
|
||||||
|
if (!shell_exec('which '.$this->config->avconv)) {
|
||||||
|
throw(new \Exception('Can\'t find avconv or ffmpeg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$procBuilder = ProcessBuilder::create(
|
||||||
|
[
|
||||||
|
$this->config->avconv,
|
||||||
|
'-v', 'quiet',
|
||||||
|
'-i', $video->url,
|
||||||
|
'-f', $video->ext,
|
||||||
|
'-c', 'copy',
|
||||||
|
'-bsf:a', 'aac_adtstoasc',
|
||||||
|
'-movflags', 'frag_keyframe+empty_moov',
|
||||||
|
'pipe:1',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
//dump($procBuilder->getProcess()->getCommandLine()); die;
|
||||||
|
|
||||||
|
return popen($procBuilder->getProcess()->getCommandLine(), 'r');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ class FrontController
|
||||||
}
|
}
|
||||||
if (isset($params['audio'])) {
|
if (isset($params['audio'])) {
|
||||||
try {
|
try {
|
||||||
return $this->getStream($params['url'], 'mp3[protocol^=http]', $response, $request, $password);
|
return $this->getStream($params['url'], 'mp3', $response, $request, $password);
|
||||||
} catch (PasswordException $e) {
|
} catch (PasswordException $e) {
|
||||||
return $this->password($request, $response);
|
return $this->password($request, $response);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -223,14 +223,22 @@ class FrontController
|
||||||
$format = 'best';
|
$format = 'best';
|
||||||
}
|
}
|
||||||
$video = $this->download->getJSON($url, $format, $password);
|
$video = $this->download->getJSON($url, $format, $password);
|
||||||
$client = new \GuzzleHttp\Client();
|
if ($video->protocol == 'm3u8') {
|
||||||
$stream = $client->request('GET', $video->url, ['stream' => true]);
|
$stream = $this->download->getM3uStream($video);
|
||||||
$response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"');
|
$response = $response->withHeader('Content-Type', 'video/'.$video->ext);
|
||||||
$response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
|
if ($request->isGet()) {
|
||||||
$response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
|
$response = $response->withBody(new Stream($stream));
|
||||||
if ($request->isGet()) {
|
}
|
||||||
$response = $response->withBody($stream->getBody());
|
} else {
|
||||||
|
$client = new \GuzzleHttp\Client();
|
||||||
|
$stream = $client->request('GET', $video->url, ['stream' => true]);
|
||||||
|
$response = $response->withHeader('Content-Type', $stream->getHeader('Content-Type'));
|
||||||
|
$response = $response->withHeader('Content-Length', $stream->getHeader('Content-Length'));
|
||||||
|
if ($request->isGet()) {
|
||||||
|
$response = $response->withBody($stream->getBody());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,18 +26,17 @@
|
||||||
<input type="hidden" name="url" value="{$video->webpage_url}" />
|
<input type="hidden" name="url" value="{$video->webpage_url}" />
|
||||||
<select name="format" id="format" class="formats monospace">
|
<select name="format" id="format" class="formats monospace">
|
||||||
<optgroup label="Generic formats">
|
<optgroup label="Generic formats">
|
||||||
<option value="best[protocol^=http]">
|
<option value="best">
|
||||||
{strip}
|
{strip}
|
||||||
Best ({$video->ext})
|
Best ({$video->ext})
|
||||||
{/strip}
|
{/strip}
|
||||||
</option>
|
</option>
|
||||||
<option value="worst[protocol^=http]">
|
<option value="worst">
|
||||||
Worst
|
Worst
|
||||||
</option>
|
</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Detailed formats" class="monospace">
|
<optgroup label="Detailed formats" class="monospace">
|
||||||
{foreach $video->formats as $format}
|
{foreach $video->formats as $format}
|
||||||
{if $format->protocol|in_array:array('http', 'https')}
|
|
||||||
{strip}
|
{strip}
|
||||||
<option value="{$format->format_id}">
|
<option value="{$format->format_id}">
|
||||||
{$format->ext}
|
{$format->ext}
|
||||||
|
@ -70,14 +69,13 @@
|
||||||
({$format->format_id})
|
({$format->format_id})
|
||||||
</option>
|
</option>
|
||||||
{/strip}
|
{/strip}
|
||||||
{/if}
|
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select><br/><br/>
|
</select><br/><br/>
|
||||||
<input class="downloadBtn" type="submit" value="Download" /><br/>
|
<input class="downloadBtn" type="submit" value="Download" /><br/>
|
||||||
</form>
|
</form>
|
||||||
{else}
|
{else}
|
||||||
<input type="hidden" name="format" value="best[protocol^=http]" />
|
<input type="hidden" name="format" value="best" />
|
||||||
<a class="downloadBtn"
|
<a class="downloadBtn"
|
||||||
href="{$video->url|escape}">Download</a><br/>
|
href="{$video->url|escape}">Download</a><br/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -176,6 +176,13 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase
|
||||||
'edgefcs.net',
|
'edgefcs.net',
|
||||||
'GRIP sucht den Sommerkönig-folge-203-0.mp3',
|
'GRIP sucht den Sommerkönig-folge-203-0.mp3',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
//Only works on the feature/stream branch
|
||||||
|
'https://twitter.com/verge/status/813055465324056576/video/1', null,
|
||||||
|
'The Verge - This tiny origami robot can self-fold and complete tasks-813055465324056576.mp4',
|
||||||
|
'video.twimg.com',
|
||||||
|
'The Verge - This tiny origami robot can self-fold and complete tasks-813055465324056576.mp3',
|
||||||
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue