diff --git a/classes/Config.php b/classes/Config.php index c57c848..14c42c0 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -38,7 +38,12 @@ class Config * * @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. diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 2284840..18de6d5 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -292,4 +292,28 @@ class VideoDownload 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'); + } } diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 19c8342..5f7ef09 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -153,7 +153,7 @@ class FrontController } if (isset($params['audio'])) { try { - return $this->getStream($params['url'], 'mp3[protocol^=http]', $response, $request, $password); + return $this->getStream($params['url'], 'mp3', $response, $request, $password); } catch (PasswordException $e) { return $this->password($request, $response); } catch (\Exception $e) { @@ -223,14 +223,22 @@ class FrontController $format = 'best'; } $video = $this->download->getJSON($url, $format, $password); - $client = new \GuzzleHttp\Client(); - $stream = $client->request('GET', $video->url, ['stream' => true]); - $response = $response->withHeader('Content-Disposition', 'attachment; filename="'.$video->_filename.'"'); - $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()); + if ($video->protocol == 'm3u8') { + $stream = $this->download->getM3uStream($video); + $response = $response->withHeader('Content-Type', 'video/'.$video->ext); + if ($request->isGet()) { + $response = $response->withBody(new Stream($stream)); + } + } 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; } diff --git a/templates/video.tpl b/templates/video.tpl index 6f11251..5ff0f96 100644 --- a/templates/video.tpl +++ b/templates/video.tpl @@ -26,18 +26,17 @@


{else} - + Download
{/if} diff --git a/tests/VideoDownloadTest.php b/tests/VideoDownloadTest.php index c1053bd..bd5ebe7 100644 --- a/tests/VideoDownloadTest.php +++ b/tests/VideoDownloadTest.php @@ -176,6 +176,13 @@ class VideoDownloadTest extends \PHPUnit_Framework_TestCase 'edgefcs.net', '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', + ] ]; }