From f1cf0a2cdc6f4f172ed07db896479dcddc4ce268 Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Tue, 3 Jul 2018 19:47:35 +0200 Subject: [PATCH] feat: Add a way to trim the audio --- classes/VideoDownload.php | 57 +++++++++++++++++++++------- controllers/FrontController.php | 67 ++++++++++++++++++++++++--------- css/style.css | 13 +++++-- templates/index.tpl | 13 +++++-- 4 files changed, 112 insertions(+), 38 deletions(-) diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index d357b0b..08d3ecc 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -268,27 +268,52 @@ class VideoDownload * @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 * * @throws Exception If avconv/ffmpeg is missing * * @return Process Process */ - private function getAvconvProcess(stdClass $video, $audioBitrate, $filetype = 'mp3', $audioOnly = true) - { + private function getAvconvProcess( + stdClass $video, + $audioBitrate, + $filetype = 'mp3', + $audioOnly = true, + $from = null, + $to = null + ) { if (!$this->checkCommand([$this->config->avconv, '-version'])) { throw new Exception(_('Can\'t find avconv or ffmpeg at ').$this->config->avconv.'.'); } + $durationRegex = '/(\d+:)?(\d+:)?(\d+)/'; + if ($video->protocol == 'rtmp') { - $rtmpArguments = $this->getRtmpArguments($video); + $beforeArguments = $this->getRtmpArguments($video); } else { - $rtmpArguments = []; + $beforeArguments = []; } if ($audioOnly) { - $videoArguments = ['-vn']; + $afterArguments = ['-vn']; } else { - $videoArguments = []; + $afterArguments = []; + } + + if (isset($from) && !empty($from)) { + if (!preg_match($durationRegex, $from)) { + throw new Exception(_('Invalid start time: ').$from.'.'); + } + $afterArguments[] = '-ss'; + $afterArguments[] = $from; + } + if (isset($to) && !empty($to)) { + if (!preg_match($durationRegex, $to)) { + throw new Exception(_('Invalid end time: ').$to.'.'); + } + $afterArguments[] = '-to'; + $afterArguments[] = $to; } $arguments = array_merge( @@ -296,13 +321,13 @@ class VideoDownload $this->config->avconv, '-v', $this->config->avconvVerbosity, ], - $rtmpArguments, + $beforeArguments, [ '-i', $video->url, '-f', $filetype, '-b:a', $audioBitrate.'k', ], - $videoArguments, + $afterArguments, [ 'pipe:1', ] @@ -322,13 +347,15 @@ class VideoDownload * @param string $url URL of page * @param string $format Format to use for the video * @param string $password Video password + * @param string $from Start the conversion at this time + * @param string $to End the conversion at this time * * @throws Exception If your try to convert and M3U8 video * @throws Exception If the popen stream was not created correctly * * @return resource popen stream */ - public function getAudioStream($url, $format, $password = null) + public function getAudioStream($url, $format, $password = null, $from = null, $to = null) { $video = $this->getJSON($url, $format, $password); @@ -336,13 +363,15 @@ class VideoDownload throw new Exception(_('Conversion of playlists is not supported.')); } - if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) { - throw new Exception(_('Conversion of M3U8 files is not supported.')); - } elseif ($video->protocol == 'http_dash_segments') { - throw new Exception(_('Conversion of DASH segments is not supported.')); + if (isset($video->protocol)) { + if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) { + throw new Exception(_('Conversion of M3U8 files is not supported.')); + } elseif ($video->protocol == 'http_dash_segments') { + throw new Exception(_('Conversion of DASH segments is not supported.')); + } } - $avconvProc = $this->getAvconvProcess($video, $this->config->audioBitrate); + $avconvProc = $this->getAvconvProcess($video, $this->config->audioBitrate, 'mp3', true, $from, $to); $stream = popen($avconvProc->getCommandLine(), 'r'); diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 3d1e2f5..43fbf14 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -201,7 +201,50 @@ class FrontController } /** - * Return the converted MP3 file. + * Return a converted MP3 file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * @param array $params GET query parameters + * @param string $password Video password + * + * @return Response HTTP response + */ + private function getConvertedAudioResponse(Request $request, Response $response, array $params, $password = null) + { + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"' + ); + $response = $response->withHeader('Content-Type', 'audio/mpeg'); + + if ($request->isGet() || $request->isPost()) { + try { + $process = $this->download->getAudioStream( + $params['url'], + 'bestaudio/best', + $password, + $params['from'], + $params['to'] + ); + } catch (Exception $e) { + $process = $this->download->getAudioStream( + $params['url'], + $this->defaultFormat, + $password, + $params['from'], + $params['to'] + ); + } + $response = $response->withBody(new Stream($process)); + } + + return $response; + } + + /** + * Return the MP3 file. * * @param Request $request PSR-7 request * @param Response $response PSR-7 response @@ -213,6 +256,10 @@ class FrontController private function getAudioResponse(Request $request, Response $response, array $params, $password = null) { try { + if (isset($params['from']) || isset($params['to'])) { + throw new Exception('Force convert when we need to seek.'); + } + if ($this->config->stream) { return $this->getStream($params['url'], 'mp3', $response, $request, $password); } else { @@ -223,23 +270,7 @@ class FrontController } catch (PasswordException $e) { return $this->password($request, $response); } catch (Exception $e) { - $response = $response->withHeader( - 'Content-Disposition', - 'attachment; filename="'. - $this->download->getAudioFilename($params['url'], 'bestaudio/best', $password).'"' - ); - $response = $response->withHeader('Content-Type', 'audio/mpeg'); - - if ($request->isGet() || $request->isPost()) { - try { - $process = $this->download->getAudioStream($params['url'], 'bestaudio/best', $password); - } catch (Exception $e) { - $process = $this->download->getAudioStream($params['url'], $this->defaultFormat, $password); - } - $response = $response->withBody(new Stream($process)); - } - - return $response; + return $this->getConvertedAudioResponse($request, $response, $params, $password); } } diff --git a/css/style.css b/css/style.css index 0dc7cd2..951ef4d 100644 --- a/css/style.css +++ b/css/style.css @@ -260,11 +260,10 @@ footer a:hover { width:622px; } -.mp3 p { +.mp3-inner { padding:3px; } - .audio:not(:checked), .audio:checked { left: -9999px; @@ -273,7 +272,7 @@ footer a:hover { .audio:not(:checked) + label, .audio:checked + label { cursor: pointer; - line-height:22px; + line-height:20px; padding-left: 82px; position: relative; } @@ -373,7 +372,15 @@ footer a:hover { width:73px; } +.seekOptions { + display: none; + margin-top: 15px; + text-align: center; +} +.audio:checked ~ .seekOptions { + display: block; +} /* Playlists */ diff --git a/templates/index.tpl b/templates/index.tpl index 2a5420f..e293947 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -19,9 +19,16 @@
{if $config->convert}
-

-

+
+ + +
+ From + to +
+
{/if}